mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
Change Connection UI (#269)
This allows the user to select different connections from the terminal block. Some features include: - a status bar at the top of the term block that shows your current connection - an icon next to the status bar that shows whether the connection is currently connected - the ability to click the status bar and type in a new connection in order to change the current connection --------- Co-authored-by: sawka <mike.sawka@gmail.com>
This commit is contained in:
@@ -137,6 +137,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
.connection-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
font-weight: 400;
|
||||
color: var(--main-text-color);
|
||||
border-radius: 2px;
|
||||
padding-right: 6px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-bg-color);
|
||||
}
|
||||
|
||||
.connection-icon-box {
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.connection-name {
|
||||
flex: 1 100 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.block-frame-textelems-wrapper {
|
||||
display: flex;
|
||||
flex: 1 100 auto;
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { blockViewToIcon, blockViewToName, getBlockHeaderIcon, IconButton, Input } from "@/app/block/blockutil";
|
||||
import {
|
||||
blockViewToIcon,
|
||||
blockViewToName,
|
||||
ConnectionButton,
|
||||
getBlockHeaderIcon,
|
||||
IconButton,
|
||||
Input,
|
||||
} from "@/app/block/blockutil";
|
||||
import { Button } from "@/app/element/button";
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import { atoms, globalStore, useBlockAtom, WOS } from "@/app/store/global";
|
||||
@@ -170,6 +177,8 @@ const HeaderTextElem = React.memo(({ elem }: { elem: HeaderElem }) => {
|
||||
{elem.text}
|
||||
</Button>
|
||||
);
|
||||
} else if (elem.elemtype == "connectionbutton") {
|
||||
return <ConnectionButton decl={elem} />;
|
||||
} else if (elem.elemtype == "div") {
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -168,6 +168,34 @@ export const IconButton = React.memo(({ decl, className }: { decl: HeaderIconBut
|
||||
);
|
||||
});
|
||||
|
||||
export const ConnectionButton = React.memo(({ decl }: { decl: ConnectionButton }) => {
|
||||
const buttonRef = React.useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<div ref={buttonRef} className={clsx("connection-button")} onClick={decl.onClick}>
|
||||
<span className="fa-stack connection-icon-box">
|
||||
{typeof decl.icon === "string" ? (
|
||||
<i
|
||||
className={clsx(util.makeIconClass(decl.icon, true), "fa-stack-1x")}
|
||||
style={{ color: decl.iconColor, marginRight: "2px" }}
|
||||
/>
|
||||
) : (
|
||||
decl.icon
|
||||
)}
|
||||
<i
|
||||
className="fa-slash fa-solid fa-stack-1x"
|
||||
style={{
|
||||
color: decl.iconColor,
|
||||
marginRight: "2px",
|
||||
textShadow: "0 1px black, 0 1.5px black",
|
||||
opacity: decl.connected ? 0 : 1,
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<div className="connection-name">{decl.text}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const Input = React.memo(({ decl, className }: { decl: HeaderInput; className: string }) => {
|
||||
const { value, ref, isDisabled, onChange, onKeyDown, onFocus, onBlur } = decl;
|
||||
return (
|
||||
|
||||
@@ -104,8 +104,9 @@ interface TypeAheadModalProps {
|
||||
suggestions?: SuggestionType[];
|
||||
label?: string;
|
||||
className?: string;
|
||||
onSelect?: (_: string) => void;
|
||||
value?: string;
|
||||
onChange?: (_: string) => void;
|
||||
onSelect?: (_: string) => void;
|
||||
onClickBackdrop?: () => void;
|
||||
onKeyDown?: (_) => void;
|
||||
}
|
||||
@@ -115,6 +116,7 @@ const TypeAheadModal = ({
|
||||
suggestions = dummy,
|
||||
label,
|
||||
anchor,
|
||||
value,
|
||||
onChange,
|
||||
onSelect,
|
||||
onClickBackdrop,
|
||||
@@ -167,6 +169,7 @@ const TypeAheadModal = ({
|
||||
<Input
|
||||
ref={inputRef}
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
autoFocus
|
||||
decoration={{
|
||||
startDecoration: (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { handleIncomingRpcMessage } from "@/app/store/wshrpc";
|
||||
import { handleIncomingRpcMessage, sendRawRpcMessage } from "@/app/store/wshrpc";
|
||||
import {
|
||||
getLayoutModelForTabById,
|
||||
LayoutTreeActionType,
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "@/layout/index";
|
||||
import { getWebServerEndpoint, getWSServerEndpoint } from "@/util/endpoints";
|
||||
import { fetch } from "@/util/fetchutil";
|
||||
import * as util from "@/util/util";
|
||||
import { produce } from "immer";
|
||||
import * as jotai from "jotai";
|
||||
import * as rxjs from "rxjs";
|
||||
@@ -25,6 +26,7 @@ let atoms: GlobalAtomsType;
|
||||
let globalEnvironment: "electron" | "renderer";
|
||||
const blockViewModelMap = new Map<string, ViewModel>();
|
||||
const Counters = new Map<string, number>();
|
||||
const ConnStatusMap = new Map<string, jotai.PrimitiveAtom<ConnStatus>>();
|
||||
|
||||
type GlobalInitOptions = {
|
||||
platform: NodeJS.Platform;
|
||||
@@ -143,9 +145,16 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
|
||||
};
|
||||
}
|
||||
|
||||
type WaveEventSubjectContainer = {
|
||||
id: string;
|
||||
handler: (event: WaveEvent) => void;
|
||||
scope: string;
|
||||
};
|
||||
|
||||
// key is "eventType" or "eventType|oref"
|
||||
const eventSubjects = new Map<string, SubjectWithRef<WSEventType>>();
|
||||
const fileSubjects = new Map<string, SubjectWithRef<WSFileEventData>>();
|
||||
const waveEventSubjects = new Map<string, WaveEventSubjectContainer[]>();
|
||||
|
||||
function getSubjectInternal(subjectKey: string): SubjectWithRef<WSEventType> {
|
||||
let subject = eventSubjects.get(subjectKey);
|
||||
@@ -173,6 +182,61 @@ function getEventORefSubject(eventType: string, oref: string): SubjectWithRef<WS
|
||||
return getSubjectInternal(eventType + "|" + oref);
|
||||
}
|
||||
|
||||
function makeWaveReSubCommand(eventType: string): RpcMessage {
|
||||
let subjects = waveEventSubjects.get(eventType);
|
||||
if (subjects == null) {
|
||||
return { command: "eventunsub", data: eventType };
|
||||
}
|
||||
let subreq: SubscriptionRequest = { event: eventType, scopes: [], allscopes: false };
|
||||
for (const scont of subjects) {
|
||||
if (util.isBlank(scont.scope)) {
|
||||
subreq.allscopes = true;
|
||||
subreq.scopes = [];
|
||||
break;
|
||||
}
|
||||
subreq.scopes.push(scont.scope);
|
||||
}
|
||||
return { command: "eventsub", data: subreq };
|
||||
}
|
||||
|
||||
function updateWaveEventSub(eventType: string) {
|
||||
const command = makeWaveReSubCommand(eventType);
|
||||
sendRawRpcMessage(command);
|
||||
}
|
||||
|
||||
function waveEventSubscribe(eventType: string, scope: string, handler: (event: WaveEvent) => void): () => void {
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
const id = crypto.randomUUID();
|
||||
const subject = new rxjs.Subject() as any;
|
||||
const scont: WaveEventSubjectContainer = { id, scope, handler };
|
||||
let subjects = waveEventSubjects.get(eventType);
|
||||
if (subjects == null) {
|
||||
subjects = [];
|
||||
waveEventSubjects.set(eventType, subjects);
|
||||
}
|
||||
subjects.push(scont);
|
||||
updateWaveEventSub(eventType);
|
||||
return () => waveEventUnsubscribe(eventType, id);
|
||||
}
|
||||
|
||||
function waveEventUnsubscribe(eventType: string, id: string) {
|
||||
let subjects = waveEventSubjects.get(eventType);
|
||||
if (subjects == null) {
|
||||
return;
|
||||
}
|
||||
const idx = subjects.findIndex((s) => s.id === id);
|
||||
if (idx === -1) {
|
||||
return;
|
||||
}
|
||||
subjects.splice(idx, 1);
|
||||
if (subjects.length === 0) {
|
||||
waveEventSubjects.delete(eventType);
|
||||
}
|
||||
updateWaveEventSub(eventType);
|
||||
}
|
||||
|
||||
function getFileSubject(zoneId: string, fileName: string): SubjectWithRef<WSFileEventData> {
|
||||
const subjectKey = zoneId + "|" + fileName;
|
||||
let subject = fileSubjects.get(subjectKey);
|
||||
@@ -251,6 +315,25 @@ function useBlockDataLoaded(blockId: string): boolean {
|
||||
|
||||
let globalWS: WSControl = null;
|
||||
|
||||
function handleWaveEvent(event: WaveEvent) {
|
||||
const subjects = waveEventSubjects.get(event.event);
|
||||
if (subjects == null) {
|
||||
return;
|
||||
}
|
||||
for (const scont of subjects) {
|
||||
if (util.isBlank(scont.scope)) {
|
||||
scont.handler(event);
|
||||
continue;
|
||||
}
|
||||
if (event.scopes == null) {
|
||||
continue;
|
||||
}
|
||||
if (event.scopes.includes(scont.scope)) {
|
||||
scont.handler(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleWSEventMessage(msg: WSEventType) {
|
||||
if (msg.eventtype == null) {
|
||||
console.log("unsupported event", msg);
|
||||
@@ -275,7 +358,7 @@ function handleWSEventMessage(msg: WSEventType) {
|
||||
}
|
||||
if (msg.eventtype == "rpc") {
|
||||
const rpcMsg: RpcMessage = msg.data;
|
||||
handleIncomingRpcMessage(rpcMsg);
|
||||
handleIncomingRpcMessage(rpcMsg, handleWaveEvent);
|
||||
return;
|
||||
}
|
||||
if (msg.eventtype == "layoutaction") {
|
||||
@@ -496,6 +579,38 @@ function countersPrint() {
|
||||
console.log(outStr);
|
||||
}
|
||||
|
||||
async function loadConnStatus() {
|
||||
const connStatusArr = await services.ClientService.GetAllConnStatus();
|
||||
if (connStatusArr == null) {
|
||||
return;
|
||||
}
|
||||
for (const connStatus of connStatusArr) {
|
||||
const curAtom = getConnStatusAtom(connStatus.connection);
|
||||
globalStore.set(curAtom, connStatus);
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeToConnEvents() {
|
||||
waveEventSubscribe("connchange", null, (event: WaveEvent) => {
|
||||
const connStatus = event.data as ConnStatus;
|
||||
if (connStatus == null || util.isBlank(connStatus.connection)) {
|
||||
return;
|
||||
}
|
||||
let curAtom = ConnStatusMap.get(connStatus.connection);
|
||||
globalStore.set(curAtom, connStatus);
|
||||
});
|
||||
}
|
||||
|
||||
function getConnStatusAtom(conn: string): jotai.PrimitiveAtom<ConnStatus> {
|
||||
let rtn = ConnStatusMap.get(conn);
|
||||
if (rtn == null) {
|
||||
const connStatus: ConnStatus = { connection: conn, connected: false, error: null };
|
||||
rtn = jotai.atom(connStatus);
|
||||
ConnStatusMap.set(conn, rtn);
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
export {
|
||||
atoms,
|
||||
counterInc,
|
||||
@@ -504,6 +619,7 @@ export {
|
||||
createBlock,
|
||||
fetchWaveFile,
|
||||
getApi,
|
||||
getConnStatusAtom,
|
||||
getEventORefSubject,
|
||||
getEventSubject,
|
||||
getFileSubject,
|
||||
@@ -514,16 +630,20 @@ export {
|
||||
initGlobal,
|
||||
initWS,
|
||||
isDev,
|
||||
loadConnStatus,
|
||||
openLink,
|
||||
PLATFORM,
|
||||
registerViewModel,
|
||||
sendWSCommand,
|
||||
setBlockFocus,
|
||||
setPlatform,
|
||||
subscribeToConnEvents,
|
||||
unregisterViewModel,
|
||||
useBlockAtom,
|
||||
useBlockCache,
|
||||
useBlockDataLoaded,
|
||||
useSettingsAtom,
|
||||
waveEventSubscribe,
|
||||
waveEventUnsubscribe,
|
||||
WOS,
|
||||
};
|
||||
|
||||
@@ -32,6 +32,9 @@ class ClientServiceType {
|
||||
FocusWindow(arg2: string): Promise<void> {
|
||||
return WOS.callBackendService("client", "FocusWindow", Array.from(arguments))
|
||||
}
|
||||
GetAllConnStatus(): Promise<ConnStatus[]> {
|
||||
return WOS.callBackendService("client", "GetAllConnStatus", Array.from(arguments))
|
||||
}
|
||||
GetClientData(): Promise<Client> {
|
||||
return WOS.callBackendService("client", "GetClientData", Array.from(arguments))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ type RpcEntry = {
|
||||
msgFn: (msg: RpcMessage) => void;
|
||||
};
|
||||
|
||||
let openRpcs = new Map<string, RpcEntry>();
|
||||
const openRpcs = new Map<string, RpcEntry>();
|
||||
|
||||
async function* rpcResponseGenerator(
|
||||
command: string,
|
||||
@@ -86,10 +86,23 @@ function sendRpcCommand(msg: RpcMessage): AsyncGenerator<RpcMessage, void, boole
|
||||
return rtnGen;
|
||||
}
|
||||
|
||||
function handleIncomingRpcMessage(msg: RpcMessage) {
|
||||
function sendRawRpcMessage(msg: RpcMessage) {
|
||||
const wsMsg: WSRpcCommand = { wscommand: "rpc", message: msg };
|
||||
globalWS.pushMessage(wsMsg);
|
||||
}
|
||||
|
||||
function handleIncomingRpcMessage(msg: RpcMessage, eventHandlerFn: (event: WaveEvent) => void) {
|
||||
const isRequest = msg.command != null || msg.reqid != null;
|
||||
if (isRequest) {
|
||||
console.log("rpc request not supported", msg);
|
||||
// handle events
|
||||
if (msg.command == "eventrecv") {
|
||||
if (eventHandlerFn != null) {
|
||||
eventHandlerFn(msg.data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("rpc command not supported", msg);
|
||||
return;
|
||||
}
|
||||
if (msg.resid == null) {
|
||||
@@ -122,4 +135,4 @@ if (globalThis.window != null) {
|
||||
globalThis["consumeGenerator"] = consumeGenerator;
|
||||
}
|
||||
|
||||
export { handleIncomingRpcMessage, sendRpcCommand };
|
||||
export { handleIncomingRpcMessage, sendRawRpcMessage, sendRpcCommand };
|
||||
|
||||
@@ -53,7 +53,7 @@ class WshServerType {
|
||||
}
|
||||
|
||||
// command "eventunsub" [call]
|
||||
EventUnsubCommand(data: SubscriptionRequest, opts?: RpcOpts): Promise<void> {
|
||||
EventUnsubCommand(data: string, opts?: RpcOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("eventunsub", data, opts);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.connection-btn {
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
background-color: orangered;
|
||||
justify-content: flex-start;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.view-term {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { TypeAheadModal } from "@/app/modals/typeaheadmodal";
|
||||
import { WshServer } from "@/app/store/wshserver";
|
||||
import { VDomView } from "@/app/view/term/vdom";
|
||||
import { WOS, atoms, getEventORefSubject, globalStore, useBlockAtom, useSettingsAtom } from "@/store/global";
|
||||
import {
|
||||
WOS,
|
||||
atoms,
|
||||
getConnStatusAtom,
|
||||
getEventORefSubject,
|
||||
globalStore,
|
||||
useBlockAtom,
|
||||
useSettingsAtom,
|
||||
} from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
import * as keyutil from "@/util/keyutil";
|
||||
import * as util from "@/util/util";
|
||||
@@ -109,13 +118,16 @@ function setBlockFocus(blockId: string) {
|
||||
|
||||
class TermViewModel {
|
||||
viewType: string;
|
||||
connected: boolean;
|
||||
termRef: React.RefObject<TermWrap>;
|
||||
blockAtom: jotai.Atom<Block>;
|
||||
termMode: jotai.Atom<string>;
|
||||
connectedAtom: jotai.Atom<boolean>;
|
||||
typeahead: boolean;
|
||||
htmlElemFocusRef: React.RefObject<HTMLInputElement>;
|
||||
blockId: string;
|
||||
viewIcon: jotai.Atom<string>;
|
||||
viewText: jotai.Atom<string>;
|
||||
viewText: jotai.Atom<HeaderElem[]>;
|
||||
viewName: jotai.Atom<string>;
|
||||
blockBg: jotai.Atom<MetaType>;
|
||||
|
||||
@@ -123,6 +135,14 @@ class TermViewModel {
|
||||
this.viewType = "term";
|
||||
this.blockId = blockId;
|
||||
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
||||
this.connectedAtom = jotai.atom((get) => {
|
||||
const connectionName = get(this.blockAtom).meta?.connection || "";
|
||||
if (connectionName == "") {
|
||||
return true;
|
||||
}
|
||||
const status = get(getConnStatusAtom(connectionName));
|
||||
return status.connected;
|
||||
});
|
||||
this.termMode = jotai.atom((get) => {
|
||||
const blockData = get(this.blockAtom);
|
||||
return blockData?.meta?.["term:mode"] ?? "term";
|
||||
@@ -139,7 +159,30 @@ class TermViewModel {
|
||||
});
|
||||
this.viewText = jotai.atom((get) => {
|
||||
const blockData = get(this.blockAtom);
|
||||
return blockData?.meta?.title ?? "";
|
||||
const titleText: HeaderText = { elemtype: "text", text: blockData?.meta?.title ?? "" };
|
||||
const typeAhead = get(atoms.typeAheadModalAtom);
|
||||
const connectionName = blockData?.meta?.connection || "";
|
||||
const isConnected = get(this.connectedAtom);
|
||||
let iconColor: string;
|
||||
if (connectionName != "") {
|
||||
iconColor = "#53b4ea";
|
||||
} else {
|
||||
iconColor = "var(--grey-text-color)";
|
||||
}
|
||||
const connButton: ConnectionButton = {
|
||||
elemtype: "connectionbutton",
|
||||
icon: "arrow-right-arrow-left",
|
||||
iconColor: iconColor,
|
||||
text: connectionName,
|
||||
connected: isConnected,
|
||||
onClick: () => {
|
||||
globalStore.set(atoms.typeAheadModalAtom, {
|
||||
...(typeAhead as TypeAheadModalType),
|
||||
[blockId]: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
return [connButton, titleText] as HeaderElem[];
|
||||
});
|
||||
this.blockBg = jotai.atom((get) => {
|
||||
const blockData = get(this.blockAtom);
|
||||
@@ -152,6 +195,10 @@ class TermViewModel {
|
||||
});
|
||||
}
|
||||
|
||||
resetConnection() {
|
||||
WshServer.ControllerRestartCommand({ blockid: this.blockId });
|
||||
}
|
||||
|
||||
giveFocus(): boolean {
|
||||
let termMode = globalStore.get(this.termMode);
|
||||
if (termMode == "term") {
|
||||
@@ -196,6 +243,9 @@ interface TerminalViewProps {
|
||||
}
|
||||
|
||||
const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
||||
const typeAhead = jotai.useAtomValue(atoms.typeAheadModalAtom);
|
||||
const viewRef = React.createRef<HTMLDivElement>();
|
||||
const [connSelected, setConnSelected] = React.useState("");
|
||||
const connectElemRef = React.useRef<HTMLDivElement>(null);
|
||||
const termRef = React.useRef<TermWrap>(null);
|
||||
model.termRef = termRef;
|
||||
@@ -371,11 +421,57 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
||||
}
|
||||
}
|
||||
|
||||
const changeConnection = React.useCallback(
|
||||
async (connName: string) => {
|
||||
await WshServer.SetMetaCommand({ oref: WOS.makeORef("block", blockId), meta: { connection: connName } });
|
||||
await WshServer.ControllerRestartCommand({ blockid: blockId });
|
||||
},
|
||||
[blockId]
|
||||
);
|
||||
|
||||
const handleTypeAheadKeyDown = React.useCallback(
|
||||
(waveEvent: WaveKeyboardEvent): boolean => {
|
||||
if (keyutil.checkKeyPressed(waveEvent, "Enter")) {
|
||||
changeConnection(connSelected);
|
||||
globalStore.set(atoms.typeAheadModalAtom, {
|
||||
...(typeAhead as TypeAheadModalType),
|
||||
[blockId]: false,
|
||||
});
|
||||
setConnSelected("");
|
||||
return true;
|
||||
}
|
||||
if (keyutil.checkKeyPressed(waveEvent, "Escape")) {
|
||||
globalStore.set(atoms.typeAheadModalAtom, {
|
||||
...(typeAhead as TypeAheadModalType),
|
||||
[blockId]: false,
|
||||
});
|
||||
setConnSelected("");
|
||||
model.giveFocus();
|
||||
return true;
|
||||
}
|
||||
},
|
||||
[typeAhead, model, blockId, connSelected]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("view-term", "term-mode-" + termMode, isFocused ? "is-focused" : null)}
|
||||
onKeyDown={handleKeyDown}
|
||||
ref={viewRef}
|
||||
>
|
||||
{typeAhead[blockId] && (
|
||||
<TypeAheadModal
|
||||
anchor={viewRef}
|
||||
suggestions={[]}
|
||||
onSelect={(selected: string) => {
|
||||
changeConnection(selected);
|
||||
}}
|
||||
onKeyDown={(e) => keyutil.keydownWrapper(handleTypeAheadKeyDown)(e)}
|
||||
onChange={(current: string) => setConnSelected(current)}
|
||||
value={connSelected}
|
||||
label="Switch Connection"
|
||||
/>
|
||||
)}
|
||||
<TermThemeUpdater blockId={blockId} termRef={termRef} />
|
||||
<TermStickers config={stickerConfig} />
|
||||
<div key="conntectElem" className="term-connectelem" ref={connectElemRef}></div>
|
||||
|
||||
Vendored
+11
-1
@@ -140,7 +140,7 @@ declare global {
|
||||
|
||||
type SubjectWithRef<T> = rxjs.Subject<T> & { refCount: number; release: () => void };
|
||||
|
||||
type HeaderElem = HeaderIconButton | HeaderText | HeaderInput | HeaderDiv | HeaderTextButton;
|
||||
type HeaderElem = HeaderIconButton | HeaderText | HeaderInput | HeaderDiv | HeaderTextButton | ConnectionButton;
|
||||
|
||||
type HeaderIconButton = {
|
||||
elemtype: "iconbutton";
|
||||
@@ -181,6 +181,16 @@ declare global {
|
||||
children: HeaderElem[];
|
||||
onMouseOver?: (e: React.MouseEvent<any>) => void;
|
||||
onMouseOut?: (e: React.MouseEvent<any>) => void;
|
||||
onClick?: (e: React.MouseEvent<any>) => void;
|
||||
};
|
||||
|
||||
type ConnectionButton = {
|
||||
elemtype: "connectionbutton";
|
||||
icon: string;
|
||||
text: string;
|
||||
iconColor: string;
|
||||
onClick?: (e: React.MouseEvent<any>) => void;
|
||||
connected: boolean;
|
||||
};
|
||||
|
||||
interface ViewModel {
|
||||
|
||||
Vendored
+8
@@ -158,6 +158,14 @@ declare global {
|
||||
meta: MetaType;
|
||||
};
|
||||
|
||||
// wshrpc.ConnStatus
|
||||
type ConnStatus = {
|
||||
status: string;
|
||||
connection: string;
|
||||
connected: boolean;
|
||||
error?: string;
|
||||
};
|
||||
|
||||
// wshrpc.CpuDataRequest
|
||||
type CpuDataRequest = {
|
||||
id: string;
|
||||
|
||||
+14
-1
@@ -2,7 +2,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { WshServer } from "@/app/store/wshserver";
|
||||
import { atoms, countersClear, countersPrint, getApi, globalStore, globalWS, initGlobal, initWS } from "@/store/global";
|
||||
import {
|
||||
atoms,
|
||||
countersClear,
|
||||
countersPrint,
|
||||
getApi,
|
||||
globalStore,
|
||||
globalWS,
|
||||
initGlobal,
|
||||
initWS,
|
||||
loadConnStatus,
|
||||
subscribeToConnEvents,
|
||||
} from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
import * as WOS from "@/store/wos";
|
||||
import * as keyutil from "@/util/keyutil";
|
||||
@@ -44,6 +55,8 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
const initialTab = await WOS.loadAndPinWaveObject<Tab>(WOS.makeORef("tab", waveWindow.activetabid));
|
||||
await WOS.loadAndPinWaveObject<LayoutState>(WOS.makeORef("layout", initialTab.layoutstate));
|
||||
initWS();
|
||||
await loadConnStatus();
|
||||
subscribeToConnEvents();
|
||||
const settings = await services.FileService.GetSettingsConfig();
|
||||
console.log("settings", settings);
|
||||
globalStore.set(atoms.settingsConfigAtom, settings);
|
||||
|
||||
Reference in New Issue
Block a user