mirror of
https://github.com/wavetermdev/waveterm
synced 2026-04-21 14:37:16 +00:00
big simplifications to the preview mocks for rendering blocks (#3082)
This commit is contained in:
parent
c126306da1
commit
4ee003aeeb
22 changed files with 338 additions and 440 deletions
|
|
@ -110,7 +110,8 @@ Create a new file for your view model (e.g., `frontend/app/view/myview/myview-mo
|
|||
|
||||
```typescript
|
||||
import { BlockNodeModel } from "@/app/block/blocktypes";
|
||||
import { WOS, globalStore, useBlockAtom } from "@/store/global";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { WOS, useBlockAtom } from "@/store/global";
|
||||
import * as jotai from "jotai";
|
||||
import { MyView } from "./myview";
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,8 @@ Create a new file for your view model (e.g., `frontend/app/view/myview/myview-mo
|
|||
|
||||
```typescript
|
||||
import { BlockNodeModel } from "@/app/block/blocktypes";
|
||||
import { WOS, globalStore, useBlockAtom } from "@/store/global";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { WOS, useBlockAtom } from "@/store/global";
|
||||
import * as jotai from "jotai";
|
||||
import { MyView } from "./myview";
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ import {
|
|||
} from "@/app/store/badge";
|
||||
import { ClientModel } from "@/app/store/client-model";
|
||||
import { GlobalModel } from "@/app/store/global-model";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { getTabModelByTabId, TabModelContext } from "@/app/store/tab-model";
|
||||
import { WaveEnvContext } from "@/app/waveenv/waveenv";
|
||||
import { makeWaveEnvImpl } from "@/app/waveenv/waveenvimpl";
|
||||
import { Workspace } from "@/app/workspace/workspace";
|
||||
import { getLayoutModelForStaticTab } from "@/layout/index";
|
||||
import { ContextMenuModel } from "@/store/contextmenu";
|
||||
import { atoms, createBlock, getSettingsPrefixAtom, globalStore } from "@/store/global";
|
||||
import { atoms, createBlock, getSettingsPrefixAtom } from "@/store/global";
|
||||
import { appHandleKeyDown, keyboardMouseDownHandler } from "@/store/keymodel";
|
||||
import { getElemAsStr } from "@/util/focusutil";
|
||||
import * as keyutil from "@/util/keyutil";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import { NewInstallOnboardingModal } from "@/app/onboarding/onboarding";
|
|||
import { CurrentOnboardingVersion } from "@/app/onboarding/onboarding-common";
|
||||
import { UpgradeOnboardingModal } from "@/app/onboarding/onboarding-upgrade";
|
||||
import { ClientModel } from "@/app/store/client-model";
|
||||
import { atoms, globalPrimaryTabStartup, globalStore } from "@/store/global";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { atoms, globalPrimaryTabStartup } from "@/store/global";
|
||||
import { modalsModel } from "@/store/modalmodel";
|
||||
import * as jotai from "jotai";
|
||||
import { useEffect } from "react";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { TabModel } from "@/app/store/tab-model";
|
|||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { DiffViewer } from "@/app/view/codeeditor/diffviewer";
|
||||
import type { WaveEnv, WaveEnvSubset } from "@/app/waveenv/waveenv";
|
||||
import { globalStore, WOS } from "@/store/global";
|
||||
import { globalStore } from "@/store/jotaiStore";
|
||||
import { base64ToString } from "@/util/util";
|
||||
import * as jotai from "jotai";
|
||||
import { useEffect } from "react";
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright 2025, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { tryReinjectKey } from "@/app/store/keymodel";
|
||||
import { CodeEditor } from "@/app/view/codeeditor/codeeditor";
|
||||
import { globalStore } from "@/store/global";
|
||||
import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil";
|
||||
import { fireAndForget } from "@/util/util";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import * as monaco from "monaco-editor";
|
||||
import type * as MonacoTypes from "monaco-editor";
|
||||
import * as monaco from "monaco-editor";
|
||||
import { useEffect } from "react";
|
||||
import type { SpecializedViewProps } from "./preview";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright 2025, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { Markdown } from "@/element/markdown";
|
||||
import { getOverrideConfigAtom, globalStore } from "@/store/global";
|
||||
import { getOverrideConfigAtom } from "@/store/global";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import type { SpecializedViewProps } from "./preview";
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
import { BlockNodeModel } from "@/app/block/blocktypes";
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import type { TabModel } from "@/app/store/tab-model";
|
||||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { getOverrideConfigAtom, globalStore, refocusNode } from "@/store/global";
|
||||
import { getOverrideConfigAtom, refocusNode } from "@/store/global";
|
||||
import * as WOS from "@/store/wos";
|
||||
import { goHistory, goHistoryBack, goHistoryForward } from "@/util/historyutil";
|
||||
import { checkKeyPressed } from "@/util/keyutil";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { Button } from "@/app/element/button";
|
||||
import { CenteredDiv } from "@/app/element/quickelems";
|
||||
import { globalStore } from "@/store/global";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { getWebServerEndpoint } from "@/util/endpoints";
|
||||
import { formatRemoteUri } from "@/util/waveutil";
|
||||
import { useAtomValue } from "jotai";
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { CenteredDiv } from "@/app/element/quickelems";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { BlockHeaderSuggestionControl } from "@/app/suggestion/suggestion";
|
||||
import { useWaveEnv } from "@/app/waveenv/waveenv";
|
||||
import { globalStore } from "@/store/global";
|
||||
import { isBlank, makeConnRoute } from "@/util/util";
|
||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import { memo, useEffect } from "react";
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ import type { BlockNodeModel } from "@/app/block/blocktypes";
|
|||
import { NullErrorBoundary } from "@/app/element/errorboundary";
|
||||
import { Search, useSearch } from "@/app/element/search";
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { useTabModel } from "@/app/store/tab-model";
|
||||
import { waveEventSubscribeSingle } from "@/app/store/wps";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import type { TermViewModel } from "@/app/view/term/term-model";
|
||||
import { atoms, getOverrideConfigAtom, getSettingsPrefixAtom, globalStore, WOS } from "@/store/global";
|
||||
import { atoms, getOverrideConfigAtom, getSettingsPrefixAtom, WOS } from "@/store/global";
|
||||
import { fireAndForget, useAtomValueSafe } from "@/util/util";
|
||||
import { computeBgStyleFromMeta } from "@/util/waveutil";
|
||||
import { ISearchOptions } from "@xterm/addon-search";
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ import { Button } from "@/app/element/button";
|
|||
import { Markdown } from "@/app/element/markdown";
|
||||
import { TypingIndicator } from "@/app/element/typingindicator";
|
||||
import { ClientModel } from "@/app/store/client-model";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import type { TabModel } from "@/app/store/tab-model";
|
||||
import { RpcResponseHelper, WshClient } from "@/app/store/wshclient";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { makeFeBlockRouteId } from "@/app/store/wshrouter";
|
||||
import { DefaultRouter, TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
|
||||
import { atoms, createBlock, fetchWaveFile, getApi, globalStore, WOS } from "@/store/global";
|
||||
import { atoms, createBlock, fetchWaveFile, getApi, WOS } from "@/store/global";
|
||||
import { BlockService, ObjectService } from "@/store/services";
|
||||
import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil";
|
||||
import { fireAndForget, isBlank, makeIconClass, mergeMeta } from "@/util/util";
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import { BlockNodeModel } from "@/app/block/blocktypes";
|
||||
import { Search, useSearch } from "@/app/element/search";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { getSimpleControlShiftAtom } from "@/app/store/keymodel";
|
||||
import type { TabModel } from "@/app/store/tab-model";
|
||||
import { makeORef } from "@/app/store/wos";
|
||||
|
|
@ -14,7 +15,7 @@ import {
|
|||
} from "@/app/suggestion/suggestion";
|
||||
import { MockBoundary } from "@/app/waveenv/mockboundary";
|
||||
import { useWaveEnv } from "@/app/waveenv/waveenv";
|
||||
import { globalStore, openLink } from "@/store/global";
|
||||
import { openLink } from "@/store/global";
|
||||
import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil";
|
||||
import { fireAndForget, useAtomValueSafe } from "@/util/util";
|
||||
import clsx from "clsx";
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { FlexiModal } from "@/app/modals/modal";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { RpcApi } from "@/app/store/wshclientapi";
|
||||
import { TabRpcClient } from "@/app/store/wshrpcutil";
|
||||
import { atoms, getApi, globalStore } from "@/store/global";
|
||||
import { atoms, getApi } from "@/store/global";
|
||||
import * as WOS from "@/store/wos";
|
||||
import { formatRelativeTime } from "@/util/util";
|
||||
import { useEffect, useState } from "react";
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright 2025, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ModalsRenderer } from "@/app/modals/modalsrenderer";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { AppSelectionModal } from "@/builder/app-selection-modal";
|
||||
import { BuilderWorkspace } from "@/builder/builder-workspace";
|
||||
import { ModalsRenderer } from "@/app/modals/modalsrenderer";
|
||||
import { atoms, globalStore, isDev } from "@/store/global";
|
||||
import { atoms, isDev } from "@/store/global";
|
||||
import { appHandleKeyDown } from "@/store/keymodel";
|
||||
import * as keyutil from "@/util/keyutil";
|
||||
import { isBlank } from "@/util/util";
|
||||
|
|
|
|||
45
frontend/preview/mock/mock-node-model.ts
Normal file
45
frontend/preview/mock/mock-node-model.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2026, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import type { NodeModel } from "@/layout/index";
|
||||
import { atom } from "jotai";
|
||||
|
||||
export type MockNodeModelOpts = {
|
||||
nodeId: string;
|
||||
blockId: string;
|
||||
innerRect?: { width: string; height: string };
|
||||
numLeafs?: number;
|
||||
};
|
||||
|
||||
export function makeMockNodeModel(opts: MockNodeModelOpts): NodeModel {
|
||||
const isFocusedAtom = atom(true);
|
||||
const isMagnifiedAtom = atom(false);
|
||||
|
||||
return {
|
||||
additionalProps: atom({} as any),
|
||||
innerRect: atom(opts.innerRect ?? { width: "1000px", height: "640px" }),
|
||||
blockNum: atom(1),
|
||||
numLeafs: atom(opts.numLeafs ?? 1),
|
||||
nodeId: opts.nodeId,
|
||||
blockId: opts.blockId,
|
||||
addEphemeralNodeToLayout: () => {},
|
||||
animationTimeS: atom(0),
|
||||
isResizing: atom(false),
|
||||
isFocused: isFocusedAtom,
|
||||
isMagnified: isMagnifiedAtom,
|
||||
anyMagnified: atom((get) => get(isMagnifiedAtom)),
|
||||
isEphemeral: atom(false),
|
||||
ready: atom(true),
|
||||
disablePointerEvents: atom(false),
|
||||
toggleMagnify: () => {
|
||||
globalStore.set(isMagnifiedAtom, !globalStore.get(isMagnifiedAtom));
|
||||
},
|
||||
focusNode: () => {
|
||||
globalStore.set(isFocusedAtom, true);
|
||||
},
|
||||
onClose: () => {},
|
||||
dragHandleRef: { current: null },
|
||||
displayContainerRef: { current: null },
|
||||
};
|
||||
}
|
||||
|
|
@ -9,11 +9,19 @@ import { RpcApiType } from "@/app/store/wshclientapi";
|
|||
import { WaveEnv } from "@/app/waveenv/waveenv";
|
||||
import { PlatformLinux, PlatformMacOS, PlatformWindows } from "@/util/platformutil";
|
||||
import { Atom, atom, PrimitiveAtom, useAtomValue } from "jotai";
|
||||
import { showPreviewContextMenu } from "../preview-contextmenu";
|
||||
import { MockSysinfoConnection } from "../previews/sysinfo.preview-util";
|
||||
import { DefaultFullConfig } from "./defaultconfig";
|
||||
import { DefaultMockFilesystem } from "./mockfilesystem";
|
||||
import { showPreviewContextMenu } from "../preview-contextmenu";
|
||||
import { previewElectronApi } from "./preview-electron-api";
|
||||
|
||||
export const PreviewTabId = crypto.randomUUID();
|
||||
export const PreviewWindowId = crypto.randomUUID();
|
||||
export const PreviewWorkspaceId = crypto.randomUUID();
|
||||
export const PreviewClientId = crypto.randomUUID();
|
||||
export const WebBlockId = crypto.randomUUID();
|
||||
export const SysinfoBlockId = crypto.randomUUID();
|
||||
|
||||
// What works "out of the box" in the mock environment (no MockEnv overrides needed):
|
||||
//
|
||||
// RPC calls (handled in makeMockRpc):
|
||||
|
|
@ -31,17 +39,23 @@ import { previewElectronApi } from "./preview-electron-api";
|
|||
// - rpc.UpdateWorkspaceTabIdsCommand -- updates .tabids on the Workspace WaveObj in the mock WOS
|
||||
//
|
||||
// Any other RPC call falls through to a console.log and resolves null.
|
||||
// Override specific calls via MockEnv.rpc (keys are the Command method names, e.g. "GetMetaCommand").
|
||||
// Override specific calls via MockEnv.rpc (keys are Command method names, e.g. "GetMetaCommand").
|
||||
// Override specific streaming calls via MockEnv.rpcStreaming (same key names, handler returns AsyncGenerator).
|
||||
//
|
||||
// Backend service calls (handled in callBackendService):
|
||||
// Any call falls through to a console.log and resolves null.
|
||||
// Override specific calls via MockEnv.services: { Service: { Method: impl } }
|
||||
// e.g. { "block": { "GetControllerStatus": (blockId) => myStatus } }
|
||||
|
||||
type RpcOverrides = {
|
||||
[K in keyof RpcApiType as K extends `${string}Command` ? K : never]?: (
|
||||
...args: any[]
|
||||
) => Promise<any> | AsyncGenerator<any, void, boolean>;
|
||||
export type RpcHandlerType = (...args: any[]) => Promise<any>;
|
||||
export type RpcStreamHandlerType = (...args: any[]) => AsyncGenerator<any, void, boolean>;
|
||||
|
||||
export type RpcOverrides = {
|
||||
[K in keyof RpcApiType as K extends `${string}Command` ? K : never]?: RpcHandlerType;
|
||||
};
|
||||
|
||||
export type RpcStreamOverrides = {
|
||||
[K in keyof RpcApiType as K extends `${string}Command` ? K : never]?: RpcStreamHandlerType;
|
||||
};
|
||||
|
||||
type ServiceOverrides = {
|
||||
|
|
@ -56,6 +70,7 @@ export type MockEnv = {
|
|||
platform?: NodeJS.Platform;
|
||||
settings?: Partial<SettingsType>;
|
||||
rpc?: RpcOverrides;
|
||||
rpcStreaming?: RpcStreamOverrides;
|
||||
services?: ServiceOverrides;
|
||||
atoms?: Partial<GlobalAtomsType>;
|
||||
electron?: Partial<ElectronApi>;
|
||||
|
|
@ -65,7 +80,11 @@ export type MockEnv = {
|
|||
mockWaveObjs?: Record<string, WaveObj>;
|
||||
};
|
||||
|
||||
export type MockWaveEnv = WaveEnv & { mockEnv: MockEnv };
|
||||
export type MockWaveEnv = WaveEnv & {
|
||||
mockEnv: MockEnv;
|
||||
addRpcOverride: <K extends keyof RpcOverrides>(command: K, handler: RpcHandlerType) => void;
|
||||
addRpcStreamOverride: <K extends keyof RpcStreamOverrides>(command: K, handler: RpcStreamHandlerType) => void;
|
||||
};
|
||||
|
||||
function mergeRecords<T>(base: Record<string, T>, overrides: Record<string, T>): Record<string, T> {
|
||||
if (base == null && overrides == null) {
|
||||
|
|
@ -91,6 +110,7 @@ export function mergeMockEnv(base: MockEnv, overrides: MockEnv): MockEnv {
|
|||
platform: overrides.platform ?? base.platform,
|
||||
settings: mergeRecords(base.settings, overrides.settings),
|
||||
rpc: mergeRecords(base.rpc as any, overrides.rpc as any) as RpcOverrides,
|
||||
rpcStreaming: mergeRecords(base.rpcStreaming as any, overrides.rpcStreaming as any) as RpcStreamOverrides,
|
||||
services: mergedServices,
|
||||
atoms: overrides.atoms != null || base.atoms != null ? { ...base.atoms, ...overrides.atoms } : undefined,
|
||||
electron:
|
||||
|
|
@ -108,7 +128,10 @@ function makeMockSettingsKeyAtom(settingsAtom: Atom<SettingsType>): WaveEnv["get
|
|||
const keyAtomCache = new Map<keyof SettingsType, Atom<any>>();
|
||||
return <T extends keyof SettingsType>(key: T) => {
|
||||
if (!keyAtomCache.has(key)) {
|
||||
keyAtomCache.set(key, atom((get) => get(settingsAtom)?.[key]));
|
||||
keyAtomCache.set(
|
||||
key,
|
||||
atom((get) => get(settingsAtom)?.[key])
|
||||
);
|
||||
}
|
||||
return keyAtomCache.get(key) as Atom<SettingsType[T]>;
|
||||
};
|
||||
|
|
@ -180,7 +203,15 @@ type MockWosFns = {
|
|||
platform: NodeJS.Platform;
|
||||
};
|
||||
|
||||
export function makeMockRpc(overrides: RpcOverrides, wos: MockWosFns): RpcApiType {
|
||||
export function makeMockRpc(
|
||||
overrides: RpcOverrides,
|
||||
streamOverrides: RpcStreamOverrides,
|
||||
wos: MockWosFns
|
||||
): {
|
||||
rpc: RpcApiType;
|
||||
setRpcHandler: (command: string, fn: RpcHandlerType) => void;
|
||||
setRpcStreamHandler: (command: string, fn: RpcStreamHandlerType) => void;
|
||||
} {
|
||||
const callDispatchMap = new Map<string, (...args: any[]) => Promise<any>>();
|
||||
const streamDispatchMap = new Map<string, (...args: any[]) => AsyncGenerator<any, void, boolean>>();
|
||||
const secrets = new Map<string, string>();
|
||||
|
|
@ -288,11 +319,13 @@ export function makeMockRpc(overrides: RpcOverrides, wos: MockWosFns): RpcApiTyp
|
|||
if (overrides) {
|
||||
for (const key of Object.keys(overrides) as (keyof RpcOverrides)[]) {
|
||||
const cmdName = key.slice(0, -"Command".length).toLowerCase();
|
||||
if (cmdName === "filereadstream" || cmdName === "fileliststream") {
|
||||
setStreamHandler(cmdName, overrides[key] as (...args: any[]) => AsyncGenerator<any, void, boolean>);
|
||||
} else {
|
||||
setCallHandler(cmdName, overrides[key] as (...args: any[]) => Promise<any>);
|
||||
}
|
||||
setCallHandler(cmdName, overrides[key] as RpcHandlerType);
|
||||
}
|
||||
}
|
||||
if (streamOverrides) {
|
||||
for (const key of Object.keys(streamOverrides) as (keyof RpcStreamOverrides)[]) {
|
||||
const cmdName = key.slice(0, -"Command".length).toLowerCase();
|
||||
setStreamHandler(cmdName, streamOverrides[key] as RpcStreamHandlerType);
|
||||
}
|
||||
}
|
||||
const rpc = new RpcApiType();
|
||||
|
|
@ -320,7 +353,17 @@ export function makeMockRpc(overrides: RpcOverrides, wos: MockWosFns): RpcApiTyp
|
|||
yield null;
|
||||
},
|
||||
});
|
||||
return rpc;
|
||||
return {
|
||||
rpc,
|
||||
setRpcHandler: (command: string, fn: RpcHandlerType) => {
|
||||
const cmdName = command.endsWith("Command") ? command.slice(0, -"Command".length).toLowerCase() : command;
|
||||
setCallHandler(cmdName, fn);
|
||||
},
|
||||
setRpcStreamHandler: (command: string, fn: RpcStreamHandlerType) => {
|
||||
const cmdName = command.endsWith("Command") ? command.slice(0, -"Command".length).toLowerCase() : command;
|
||||
setStreamHandler(cmdName, fn);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function applyMockEnvOverrides(env: WaveEnv, newOverrides: MockEnv): MockWaveEnv {
|
||||
|
|
@ -331,7 +374,57 @@ export function applyMockEnvOverrides(env: WaveEnv, newOverrides: MockEnv): Mock
|
|||
|
||||
export function makeMockWaveEnv(mockEnv?: MockEnv): MockWaveEnv {
|
||||
const overrides: MockEnv = mockEnv ?? {};
|
||||
const platform = overrides.platform ?? PlatformMacOS;
|
||||
const tabId = overrides.tabId ?? PreviewTabId;
|
||||
const defaultMockWaveObjs: Record<string, WaveObj> = {
|
||||
[`workspace:${PreviewWorkspaceId}`]: {
|
||||
otype: "workspace",
|
||||
oid: PreviewWorkspaceId,
|
||||
version: 1,
|
||||
name: "Preview Workspace",
|
||||
tabids: [PreviewTabId],
|
||||
activetabid: PreviewTabId,
|
||||
meta: {},
|
||||
} as Workspace,
|
||||
[`tab:${PreviewTabId}`]: {
|
||||
otype: "tab",
|
||||
oid: PreviewTabId,
|
||||
version: 1,
|
||||
name: "Preview Tab",
|
||||
blockids: [WebBlockId, SysinfoBlockId],
|
||||
meta: {},
|
||||
} as Tab,
|
||||
[`block:${WebBlockId}`]: {
|
||||
otype: "block",
|
||||
oid: WebBlockId,
|
||||
version: 1,
|
||||
meta: {
|
||||
view: "web",
|
||||
},
|
||||
} as Block,
|
||||
[`block:${SysinfoBlockId}`]: {
|
||||
otype: "block",
|
||||
oid: SysinfoBlockId,
|
||||
version: 1,
|
||||
meta: {
|
||||
view: "sysinfo",
|
||||
connection: MockSysinfoConnection,
|
||||
"sysinfo:type": "CPU + Mem",
|
||||
"graph:numpoints": 90,
|
||||
},
|
||||
} as Block,
|
||||
};
|
||||
const defaultAtoms: Partial<GlobalAtomsType> = {
|
||||
uiContext: atom({ windowid: PreviewWindowId, activetabid: PreviewTabId } as UIContext),
|
||||
staticTabId: atom(PreviewTabId),
|
||||
workspaceId: atom(PreviewWorkspaceId),
|
||||
};
|
||||
const mergedOverrides: MockEnv = {
|
||||
...overrides,
|
||||
tabId,
|
||||
mockWaveObjs: { ...defaultMockWaveObjs, ...(overrides.mockWaveObjs ?? {}) },
|
||||
atoms: { ...defaultAtoms, ...(overrides.atoms ?? {}) },
|
||||
};
|
||||
const platform = mergedOverrides.platform ?? PlatformMacOS;
|
||||
const connStatusAtomCache = new Map<string, PrimitiveAtom<ConnStatus>>();
|
||||
const waveObjectValueAtomCache = new Map<string, PrimitiveAtom<any>>();
|
||||
const waveObjectDerivedAtomCache = new Map<string, Atom<any>>();
|
||||
|
|
@ -339,12 +432,17 @@ export function makeMockWaveEnv(mockEnv?: MockEnv): MockWaveEnv {
|
|||
const connConfigKeyAtomCache = new Map<string, Atom<any>>();
|
||||
const getWaveObjectAtom = <T extends WaveObj>(oref: string): PrimitiveAtom<T> => {
|
||||
if (!waveObjectValueAtomCache.has(oref)) {
|
||||
const obj = (overrides.mockWaveObjs?.[oref] ?? null) as T;
|
||||
const obj = (mergedOverrides.mockWaveObjs?.[oref] ?? null) as T;
|
||||
waveObjectValueAtomCache.set(oref, atom(obj) as PrimitiveAtom<T>);
|
||||
}
|
||||
return waveObjectValueAtomCache.get(oref) as PrimitiveAtom<T>;
|
||||
};
|
||||
const atoms = makeMockGlobalAtoms(overrides.settings, overrides.atoms, overrides.tabId, getWaveObjectAtom);
|
||||
const atoms = makeMockGlobalAtoms(
|
||||
mergedOverrides.settings,
|
||||
mergedOverrides.atoms,
|
||||
mergedOverrides.tabId,
|
||||
getWaveObjectAtom
|
||||
);
|
||||
const localHostDisplayNameAtom = atom<string>((get) => {
|
||||
const configValue = get(atoms.settingsAtom)?.["conn:localhostdisplayname"];
|
||||
if (configValue != null) {
|
||||
|
|
@ -363,38 +461,55 @@ export function makeMockWaveEnv(mockEnv?: MockEnv): MockWaveEnv {
|
|||
globalStore.set(waveObjectValueAtomCache.get(oref), obj);
|
||||
},
|
||||
};
|
||||
const { rpc, setRpcHandler, setRpcStreamHandler } = makeMockRpc(mergedOverrides.rpc, mergedOverrides.rpcStreaming, mockWosFns);
|
||||
const env = {
|
||||
isMock: true,
|
||||
mockEnv: overrides,
|
||||
mockEnv: mergedOverrides,
|
||||
electron: {
|
||||
...previewElectronApi,
|
||||
getPlatform: () => platform,
|
||||
openExternal: (url: string) => {
|
||||
window.open(url, "_blank");
|
||||
},
|
||||
...overrides.electron,
|
||||
...mergedOverrides.electron,
|
||||
},
|
||||
rpc: makeMockRpc(overrides.rpc, mockWosFns),
|
||||
rpc,
|
||||
atoms,
|
||||
getSettingsKeyAtom: makeMockSettingsKeyAtom(atoms.settingsAtom),
|
||||
platform,
|
||||
isDev: () => overrides.isDev ?? true,
|
||||
isDev: () => mergedOverrides.isDev ?? true,
|
||||
isWindows: () => platform === PlatformWindows,
|
||||
isMacOS: () => platform === PlatformMacOS,
|
||||
createBlock:
|
||||
overrides.createBlock ??
|
||||
mergedOverrides.createBlock ??
|
||||
((blockDef: BlockDef, magnified?: boolean, ephemeral?: boolean) => {
|
||||
console.log("[mock createBlock]", blockDef, { magnified, ephemeral });
|
||||
return Promise.resolve(crypto.randomUUID());
|
||||
const newBlockId = crypto.randomUUID();
|
||||
const newBlock: Block = {
|
||||
otype: "block",
|
||||
oid: newBlockId,
|
||||
version: 1,
|
||||
meta: blockDef.meta ?? {},
|
||||
};
|
||||
mockWosFns.mockSetWaveObj(`block:${newBlockId}`, newBlock);
|
||||
const tabORef = `tab:${tabId}`;
|
||||
const tabAtom = getWaveObjectAtom<Tab>(tabORef);
|
||||
const currentTab = globalStore.get(tabAtom);
|
||||
if (currentTab != null) {
|
||||
mockWosFns.mockSetWaveObj(tabORef, {
|
||||
...currentTab,
|
||||
blockids: [...(currentTab.blockids ?? []), newBlockId],
|
||||
});
|
||||
}
|
||||
return Promise.resolve(newBlockId);
|
||||
}),
|
||||
showContextMenu:
|
||||
overrides.showContextMenu ?? showPreviewContextMenu,
|
||||
showContextMenu: mergedOverrides.showContextMenu ?? showPreviewContextMenu,
|
||||
getLocalHostDisplayNameAtom: () => {
|
||||
return localHostDisplayNameAtom;
|
||||
},
|
||||
getConnStatusAtom: (conn: string) => {
|
||||
if (!connStatusAtomCache.has(conn)) {
|
||||
const connStatus = overrides.connStatus?.[conn] ?? makeDefaultConnStatus(conn);
|
||||
const connStatus = mergedOverrides.connStatus?.[conn] ?? makeDefaultConnStatus(conn);
|
||||
connStatusAtomCache.set(conn, atom(connStatus));
|
||||
}
|
||||
return connStatusAtomCache.get(conn);
|
||||
|
|
@ -449,7 +564,7 @@ export function makeMockWaveEnv(mockEnv?: MockEnv): MockWaveEnv {
|
|||
},
|
||||
services: null as any,
|
||||
callBackendService: (service: string, method: string, args: any[], noUIContext?: boolean) => {
|
||||
const fn = overrides.services?.[service]?.[method];
|
||||
const fn = mergedOverrides.services?.[service]?.[method];
|
||||
if (fn) {
|
||||
return fn(...args);
|
||||
}
|
||||
|
|
@ -458,6 +573,12 @@ export function makeMockWaveEnv(mockEnv?: MockEnv): MockWaveEnv {
|
|||
},
|
||||
mockSetWaveObj: mockWosFns.mockSetWaveObj,
|
||||
mockModels: new Map<any, any>(),
|
||||
addRpcOverride: <K extends keyof RpcOverrides>(command: K, handler: RpcHandlerType) => {
|
||||
setRpcHandler(command as string, handler);
|
||||
},
|
||||
addRpcStreamOverride: <K extends keyof RpcStreamOverrides>(command: K, handler: RpcStreamHandlerType) => {
|
||||
setRpcStreamHandler(command as string, handler);
|
||||
},
|
||||
} as MockWaveEnv;
|
||||
env.services = Object.fromEntries(
|
||||
Object.entries(AllServiceTypes).map(([key, ServiceClass]) => [key, new ServiceClass(env)])
|
||||
|
|
|
|||
24
frontend/preview/mock/use-rpc-override.ts
Normal file
24
frontend/preview/mock/use-rpc-override.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2026, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useWaveEnv } from "@/app/waveenv/waveenv";
|
||||
import * as React from "react";
|
||||
import { MockWaveEnv, RpcHandlerType, RpcOverrides, RpcStreamHandlerType, RpcStreamOverrides } from "./mockwaveenv";
|
||||
|
||||
export function useRpcOverride<K extends keyof RpcOverrides>(command: K, handler: RpcHandlerType): void {
|
||||
const mockEnv = useWaveEnv() as MockWaveEnv;
|
||||
const registeredRef = React.useRef(false);
|
||||
if (!registeredRef.current) {
|
||||
registeredRef.current = true;
|
||||
mockEnv.addRpcOverride(command, handler);
|
||||
}
|
||||
}
|
||||
|
||||
export function useRpcStreamOverride<K extends keyof RpcStreamOverrides>(command: K, handler: RpcStreamHandlerType): void {
|
||||
const mockEnv = useWaveEnv() as MockWaveEnv;
|
||||
const registeredRef = React.useRef(false);
|
||||
if (!registeredRef.current) {
|
||||
registeredRef.current = true;
|
||||
mockEnv.addRpcStreamOverride(command, handler);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,12 +6,13 @@ import { ErrorBoundary } from "@/app/element/errorboundary";
|
|||
import { getAtoms, initGlobalAtoms } from "@/app/store/global-atoms";
|
||||
import { GlobalModel } from "@/app/store/global-model";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { getTabModelByTabId, TabModelContext } from "@/app/store/tab-model";
|
||||
import { WaveEnvContext } from "@/app/waveenv/waveenv";
|
||||
import { loadFonts } from "@/util/fontutil";
|
||||
import { atom, Provider } from "jotai";
|
||||
import { Provider } from "jotai";
|
||||
import React, { lazy, Suspense, useRef } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { makeMockWaveEnv } from "./mock/mockwaveenv";
|
||||
import { makeMockWaveEnv, PreviewClientId, PreviewTabId, PreviewWindowId } from "./mock/mockwaveenv";
|
||||
import { installPreviewElectronApi } from "./mock/preview-electron-api";
|
||||
import { PreviewContextMenu } from "./preview-contextmenu";
|
||||
|
||||
|
|
@ -93,22 +94,14 @@ function PreviewHeader({ previewName }: { previewName: string }) {
|
|||
}
|
||||
|
||||
function PreviewRoot() {
|
||||
const waveEnvRef = useRef(
|
||||
makeMockWaveEnv({
|
||||
atoms: {
|
||||
uiContext: atom({ windowid: PreviewWindowId, activetabid: PreviewTabId } as UIContext),
|
||||
staticTabId: atom(PreviewTabId),
|
||||
workspaceId: atom(PreviewWorkspaceId),
|
||||
},
|
||||
})
|
||||
);
|
||||
const waveEnvRef = useRef(makeMockWaveEnv());
|
||||
return (
|
||||
<Provider store={globalStore}>
|
||||
<WaveEnvContext.Provider value={waveEnvRef.current}>
|
||||
<>
|
||||
<TabModelContext.Provider value={getTabModelByTabId(PreviewTabId, waveEnvRef.current)}>
|
||||
<PreviewApp />
|
||||
<PreviewContextMenu />
|
||||
</>
|
||||
</TabModelContext.Provider>
|
||||
</WaveEnvContext.Provider>
|
||||
</Provider>
|
||||
);
|
||||
|
|
@ -150,11 +143,6 @@ function PreviewApp() {
|
|||
return <PreviewIndex />;
|
||||
}
|
||||
|
||||
const PreviewTabId = crypto.randomUUID();
|
||||
const PreviewWindowId = crypto.randomUUID();
|
||||
const PreviewWorkspaceId = crypto.randomUUID();
|
||||
const PreviewClientId = crypto.randomUUID();
|
||||
|
||||
function initPreview() {
|
||||
installPreviewElectronApi();
|
||||
const initOpts = {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { Block } from "@/app/block/block";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { getTabModelByTabId, TabModelContext } from "@/app/store/tab-model";
|
||||
import { useWaveEnv, WaveEnvContext } from "@/app/waveenv/waveenv";
|
||||
import type { NodeModel } from "@/layout/index";
|
||||
import { atom } from "jotai";
|
||||
import { useWaveEnv } from "@/app/waveenv/waveenv";
|
||||
import * as React from "react";
|
||||
import { applyMockEnvOverrides, MockWaveEnv } from "../mock/mockwaveenv";
|
||||
import { makeMockNodeModel } from "../mock/mock-node-model";
|
||||
import { useRpcOverride } from "../mock/use-rpc-override";
|
||||
import {
|
||||
DefaultAiFileDiffChatId,
|
||||
DefaultAiFileDiffFileName,
|
||||
|
|
@ -16,130 +13,51 @@ import {
|
|||
makeMockAiFileDiffResponse,
|
||||
} from "./aifilediff.preview-util";
|
||||
|
||||
const PreviewWorkspaceId = "preview-aifilediff-workspace";
|
||||
const PreviewTabId = "preview-aifilediff-tab";
|
||||
const PreviewNodeId = "preview-aifilediff-node";
|
||||
const PreviewBlockId = "preview-aifilediff-block";
|
||||
|
||||
function makeMockWorkspace(): Workspace {
|
||||
return {
|
||||
otype: "workspace",
|
||||
oid: PreviewWorkspaceId,
|
||||
version: 1,
|
||||
name: "Preview Workspace",
|
||||
tabids: [PreviewTabId],
|
||||
activetabid: PreviewTabId,
|
||||
meta: {},
|
||||
} as Workspace;
|
||||
}
|
||||
|
||||
function makeMockTab(): Tab {
|
||||
return {
|
||||
otype: "tab",
|
||||
oid: PreviewTabId,
|
||||
version: 1,
|
||||
name: "AI File Diff Preview",
|
||||
blockids: [PreviewBlockId],
|
||||
meta: {},
|
||||
} as Tab;
|
||||
}
|
||||
|
||||
function makeMockBlock(): Block {
|
||||
return {
|
||||
otype: "block",
|
||||
oid: PreviewBlockId,
|
||||
version: 1,
|
||||
meta: {
|
||||
view: "aifilediff",
|
||||
file: DefaultAiFileDiffFileName,
|
||||
"aifilediff:chatid": DefaultAiFileDiffChatId,
|
||||
"aifilediff:toolcallid": DefaultAiFileDiffToolCallId,
|
||||
},
|
||||
} as Block;
|
||||
}
|
||||
|
||||
function makePreviewNodeModel(): NodeModel {
|
||||
const isFocusedAtom = atom(true);
|
||||
const isMagnifiedAtom = atom(false);
|
||||
|
||||
return {
|
||||
additionalProps: atom({} as any),
|
||||
innerRect: atom({ width: "1000px", height: "640px" }),
|
||||
blockNum: atom(1),
|
||||
numLeafs: atom(1),
|
||||
nodeId: PreviewNodeId,
|
||||
blockId: PreviewBlockId,
|
||||
addEphemeralNodeToLayout: () => {},
|
||||
animationTimeS: atom(0),
|
||||
isResizing: atom(false),
|
||||
isFocused: isFocusedAtom,
|
||||
isMagnified: isMagnifiedAtom,
|
||||
anyMagnified: atom(false),
|
||||
isEphemeral: atom(false),
|
||||
ready: atom(true),
|
||||
disablePointerEvents: atom(false),
|
||||
toggleMagnify: () => {
|
||||
globalStore.set(isMagnifiedAtom, !globalStore.get(isMagnifiedAtom));
|
||||
},
|
||||
focusNode: () => {
|
||||
globalStore.set(isFocusedAtom, true);
|
||||
},
|
||||
onClose: () => {},
|
||||
dragHandleRef: { current: null },
|
||||
displayContainerRef: { current: null },
|
||||
};
|
||||
}
|
||||
|
||||
function AiFileDiffPreviewInner() {
|
||||
const baseEnv = useWaveEnv();
|
||||
const nodeModel = React.useMemo(() => makePreviewNodeModel(), []);
|
||||
|
||||
const env = React.useMemo<MockWaveEnv>(() => {
|
||||
const mockWaveObjs: Record<string, WaveObj> = {
|
||||
[`workspace:${PreviewWorkspaceId}`]: makeMockWorkspace(),
|
||||
[`tab:${PreviewTabId}`]: makeMockTab(),
|
||||
[`block:${PreviewBlockId}`]: makeMockBlock(),
|
||||
};
|
||||
|
||||
return applyMockEnvOverrides(baseEnv, {
|
||||
tabId: PreviewTabId,
|
||||
mockWaveObjs,
|
||||
atoms: {
|
||||
workspaceId: atom(PreviewWorkspaceId),
|
||||
staticTabId: atom(PreviewTabId),
|
||||
},
|
||||
rpc: {
|
||||
WaveAIGetToolDiffCommand: async (_client, data) => {
|
||||
if (
|
||||
data.chatid !== DefaultAiFileDiffChatId ||
|
||||
data.toolcallid !== DefaultAiFileDiffToolCallId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return makeMockAiFileDiffResponse();
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [baseEnv]);
|
||||
|
||||
const tabModel = React.useMemo(() => getTabModelByTabId(PreviewTabId, env), [env]);
|
||||
|
||||
return (
|
||||
<WaveEnvContext.Provider value={env}>
|
||||
<TabModelContext.Provider value={tabModel}>
|
||||
<div className="flex w-full max-w-[1120px] flex-col gap-2 px-6 py-6">
|
||||
<div className="text-xs text-muted font-mono">full aifilediff block (mock WOS + mock WaveAI diff RPC)</div>
|
||||
<div className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="h-[720px]">
|
||||
<Block preview={false} nodeModel={nodeModel} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabModelContext.Provider>
|
||||
</WaveEnvContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function AiFileDiffPreview() {
|
||||
return <AiFileDiffPreviewInner />;
|
||||
const env = useWaveEnv();
|
||||
const [blockId, setBlockId] = React.useState<string>(null);
|
||||
|
||||
useRpcOverride("WaveAIGetToolDiffCommand", async (_client, data) => {
|
||||
if (data.chatid !== DefaultAiFileDiffChatId || data.toolcallid !== DefaultAiFileDiffToolCallId) {
|
||||
return null;
|
||||
}
|
||||
return makeMockAiFileDiffResponse();
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
env.createBlock(
|
||||
{
|
||||
meta: {
|
||||
view: "aifilediff",
|
||||
file: DefaultAiFileDiffFileName,
|
||||
"aifilediff:chatid": DefaultAiFileDiffChatId,
|
||||
"aifilediff:toolcallid": DefaultAiFileDiffToolCallId,
|
||||
},
|
||||
},
|
||||
false,
|
||||
false
|
||||
).then((id) => setBlockId(id));
|
||||
}, []);
|
||||
|
||||
const nodeModel = React.useMemo(
|
||||
() => (blockId != null ? makeMockNodeModel({ nodeId: PreviewNodeId, blockId }) : null),
|
||||
[blockId]
|
||||
);
|
||||
|
||||
if (blockId == null || nodeModel == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-[1120px] flex-col gap-2 px-6 py-6">
|
||||
<div className="text-xs text-muted font-mono">full aifilediff block (mock WOS + mock WaveAI diff RPC)</div>
|
||||
<div className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="h-[720px]">
|
||||
<Block preview={false} nodeModel={nodeModel} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,11 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { Block } from "@/app/block/block";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { getTabModelByTabId, TabModelContext } from "@/app/store/tab-model";
|
||||
import { handleWaveEvent } from "@/app/store/wps";
|
||||
import { useWaveEnv, WaveEnvContext } from "@/app/waveenv/waveenv";
|
||||
import type { NodeModel } from "@/layout/index";
|
||||
import { atom } from "jotai";
|
||||
import * as React from "react";
|
||||
import { applyMockEnvOverrides, MockWaveEnv } from "../mock/mockwaveenv";
|
||||
import { makeMockNodeModel } from "../mock/mock-node-model";
|
||||
import { SysinfoBlockId } from "../mock/mockwaveenv";
|
||||
import { useRpcOverride } from "../mock/use-rpc-override";
|
||||
import {
|
||||
DefaultSysinfoHistoryPoints,
|
||||
makeMockSysinfoEvent,
|
||||
|
|
@ -17,112 +14,22 @@ import {
|
|||
MockSysinfoConnection,
|
||||
} from "./sysinfo.preview-util";
|
||||
|
||||
const PreviewWorkspaceId = "preview-sysinfo-workspace";
|
||||
const PreviewTabId = "preview-sysinfo-tab";
|
||||
const PreviewNodeId = "preview-sysinfo-node";
|
||||
const PreviewBlockId = "preview-sysinfo-block";
|
||||
|
||||
function makeMockWorkspace(): Workspace {
|
||||
return {
|
||||
otype: "workspace",
|
||||
oid: PreviewWorkspaceId,
|
||||
version: 1,
|
||||
name: "Preview Workspace",
|
||||
tabids: [PreviewTabId],
|
||||
activetabid: PreviewTabId,
|
||||
meta: {},
|
||||
} as Workspace;
|
||||
}
|
||||
|
||||
function makeMockTab(): Tab {
|
||||
return {
|
||||
otype: "tab",
|
||||
oid: PreviewTabId,
|
||||
version: 1,
|
||||
name: "Sysinfo Preview",
|
||||
blockids: [PreviewBlockId],
|
||||
meta: {},
|
||||
} as Tab;
|
||||
}
|
||||
|
||||
function makeMockBlock(): Block {
|
||||
return {
|
||||
otype: "block",
|
||||
oid: PreviewBlockId,
|
||||
version: 1,
|
||||
meta: {
|
||||
view: "sysinfo",
|
||||
connection: MockSysinfoConnection,
|
||||
"sysinfo:type": "CPU + Mem",
|
||||
"graph:numpoints": 90,
|
||||
},
|
||||
} as Block;
|
||||
}
|
||||
|
||||
function makePreviewNodeModel(): NodeModel {
|
||||
const isFocusedAtom = atom(true);
|
||||
const isMagnifiedAtom = atom(false);
|
||||
|
||||
return {
|
||||
additionalProps: atom({} as any),
|
||||
innerRect: atom({ width: "920px", height: "560px" }),
|
||||
blockNum: atom(1),
|
||||
numLeafs: atom(2),
|
||||
nodeId: PreviewNodeId,
|
||||
blockId: PreviewBlockId,
|
||||
addEphemeralNodeToLayout: () => {},
|
||||
animationTimeS: atom(0),
|
||||
isResizing: atom(false),
|
||||
isFocused: isFocusedAtom,
|
||||
isMagnified: isMagnifiedAtom,
|
||||
anyMagnified: atom(false),
|
||||
isEphemeral: atom(false),
|
||||
ready: atom(true),
|
||||
disablePointerEvents: atom(false),
|
||||
toggleMagnify: () => {
|
||||
globalStore.set(isMagnifiedAtom, !globalStore.get(isMagnifiedAtom));
|
||||
},
|
||||
focusNode: () => {
|
||||
globalStore.set(isFocusedAtom, true);
|
||||
},
|
||||
onClose: () => {},
|
||||
dragHandleRef: { current: null },
|
||||
displayContainerRef: { current: null },
|
||||
};
|
||||
}
|
||||
|
||||
function SysinfoPreviewInner() {
|
||||
const baseEnv = useWaveEnv();
|
||||
export default function SysinfoPreview() {
|
||||
const historyRef = React.useRef(makeMockSysinfoHistory());
|
||||
const nodeModel = React.useMemo(() => makePreviewNodeModel(), []);
|
||||
const nodeModel = React.useMemo(
|
||||
() => makeMockNodeModel({ nodeId: PreviewNodeId, blockId: SysinfoBlockId, innerRect: { width: "920px", height: "560px" }, numLeafs: 2 }),
|
||||
[]
|
||||
);
|
||||
|
||||
const env = React.useMemo<MockWaveEnv>(() => {
|
||||
const mockWaveObjs: Record<string, WaveObj> = {
|
||||
[`workspace:${PreviewWorkspaceId}`]: makeMockWorkspace(),
|
||||
[`tab:${PreviewTabId}`]: makeMockTab(),
|
||||
[`block:${PreviewBlockId}`]: makeMockBlock(),
|
||||
};
|
||||
|
||||
return applyMockEnvOverrides(baseEnv, {
|
||||
tabId: PreviewTabId,
|
||||
mockWaveObjs,
|
||||
atoms: {
|
||||
workspaceId: atom(PreviewWorkspaceId),
|
||||
staticTabId: atom(PreviewTabId),
|
||||
},
|
||||
rpc: {
|
||||
EventReadHistoryCommand: async (_client, data) => {
|
||||
if (data.event !== "sysinfo" || data.scope !== MockSysinfoConnection) {
|
||||
return [];
|
||||
}
|
||||
const maxItems = data.maxitems ?? historyRef.current.length;
|
||||
return historyRef.current.slice(-maxItems);
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [baseEnv]);
|
||||
|
||||
const tabModel = React.useMemo(() => getTabModelByTabId(PreviewTabId, env), [env]);
|
||||
useRpcOverride("EventReadHistoryCommand", async (_client, data) => {
|
||||
if (data.event !== "sysinfo" || data.scope !== MockSysinfoConnection) {
|
||||
return [];
|
||||
}
|
||||
const maxItems = data.maxitems ?? historyRef.current.length;
|
||||
return historyRef.current.slice(-maxItems);
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
let nextStep = historyRef.current.length;
|
||||
|
|
@ -141,21 +48,13 @@ function SysinfoPreviewInner() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<WaveEnvContext.Provider value={env}>
|
||||
<TabModelContext.Provider value={tabModel}>
|
||||
<div className="flex w-full max-w-[980px] flex-col gap-2 px-6 py-6">
|
||||
<div className="text-xs text-muted font-mono">full sysinfo block (mock WOS + FE-only WPS events)</div>
|
||||
<div className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="h-[620px]">
|
||||
<Block preview={false} nodeModel={nodeModel} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full max-w-[980px] flex-col gap-2 px-6 py-6">
|
||||
<div className="text-xs text-muted font-mono">full sysinfo block (mock WOS + FE-only WPS events)</div>
|
||||
<div className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="h-[620px]">
|
||||
<Block preview={false} nodeModel={nodeModel} />
|
||||
</div>
|
||||
</TabModelContext.Provider>
|
||||
</WaveEnvContext.Provider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SysinfoPreview() {
|
||||
return <SysinfoPreviewInner />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,134 +2,26 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { Block } from "@/app/block/block";
|
||||
import { globalStore } from "@/app/store/jotaiStore";
|
||||
import { getTabModelByTabId, TabModelContext } from "@/app/store/tab-model";
|
||||
import { mockObjectForPreview } from "@/app/store/wos";
|
||||
import { useWaveEnv, WaveEnvContext } from "@/app/waveenv/waveenv";
|
||||
import type { NodeModel } from "@/layout/index";
|
||||
import { atom } from "jotai";
|
||||
import * as React from "react";
|
||||
import { applyMockEnvOverrides, MockWaveEnv } from "../mock/mockwaveenv";
|
||||
import { makeMockNodeModel } from "../mock/mock-node-model";
|
||||
import { WebBlockId } from "../mock/mockwaveenv";
|
||||
|
||||
const PreviewWorkspaceId = "preview-web-workspace";
|
||||
const PreviewTabId = "preview-web-tab";
|
||||
const PreviewNodeId = "preview-web-node";
|
||||
const PreviewBlockId = "preview-web-block";
|
||||
const PreviewUrl = "https://waveterm.dev";
|
||||
|
||||
function makeMockWorkspace(): Workspace {
|
||||
return {
|
||||
otype: "workspace",
|
||||
oid: PreviewWorkspaceId,
|
||||
version: 1,
|
||||
name: "Preview Workspace",
|
||||
tabids: [PreviewTabId],
|
||||
activetabid: PreviewTabId,
|
||||
meta: {},
|
||||
} as Workspace;
|
||||
}
|
||||
|
||||
function makeMockTab(): Tab {
|
||||
return {
|
||||
otype: "tab",
|
||||
oid: PreviewTabId,
|
||||
version: 1,
|
||||
name: "Web Preview",
|
||||
blockids: [PreviewBlockId],
|
||||
meta: {},
|
||||
} as Tab;
|
||||
}
|
||||
|
||||
function makeMockBlock(): Block {
|
||||
return {
|
||||
otype: "block",
|
||||
oid: PreviewBlockId,
|
||||
version: 1,
|
||||
meta: {
|
||||
view: "web",
|
||||
url: PreviewUrl,
|
||||
},
|
||||
} as Block;
|
||||
}
|
||||
|
||||
const previewWaveObjs: Record<string, WaveObj> = {
|
||||
[`workspace:${PreviewWorkspaceId}`]: makeMockWorkspace(),
|
||||
[`tab:${PreviewTabId}`]: makeMockTab(),
|
||||
[`block:${PreviewBlockId}`]: makeMockBlock(),
|
||||
};
|
||||
|
||||
for (const [oref, obj] of Object.entries(previewWaveObjs)) {
|
||||
mockObjectForPreview(oref, obj);
|
||||
}
|
||||
|
||||
function makePreviewNodeModel(): NodeModel {
|
||||
const isFocusedAtom = atom(true);
|
||||
const isMagnifiedAtom = atom(false);
|
||||
|
||||
return {
|
||||
additionalProps: atom({} as any),
|
||||
innerRect: atom({ width: "1040px", height: "620px" }),
|
||||
blockNum: atom(1),
|
||||
numLeafs: atom(1),
|
||||
nodeId: PreviewNodeId,
|
||||
blockId: PreviewBlockId,
|
||||
addEphemeralNodeToLayout: () => {},
|
||||
animationTimeS: atom(0),
|
||||
isResizing: atom(false),
|
||||
isFocused: isFocusedAtom,
|
||||
isMagnified: isMagnifiedAtom,
|
||||
anyMagnified: atom(false),
|
||||
isEphemeral: atom(false),
|
||||
ready: atom(true),
|
||||
disablePointerEvents: atom(false),
|
||||
toggleMagnify: () => {
|
||||
globalStore.set(isMagnifiedAtom, !globalStore.get(isMagnifiedAtom));
|
||||
},
|
||||
focusNode: () => {
|
||||
globalStore.set(isFocusedAtom, true);
|
||||
},
|
||||
onClose: () => {},
|
||||
dragHandleRef: { current: null },
|
||||
displayContainerRef: { current: null },
|
||||
};
|
||||
}
|
||||
|
||||
function WebPreviewInner() {
|
||||
const baseEnv = useWaveEnv();
|
||||
const nodeModel = React.useMemo(() => makePreviewNodeModel(), []);
|
||||
|
||||
const env = React.useMemo<MockWaveEnv>(() => {
|
||||
return applyMockEnvOverrides(baseEnv, {
|
||||
tabId: PreviewTabId,
|
||||
mockWaveObjs: previewWaveObjs,
|
||||
atoms: {
|
||||
workspaceId: atom(PreviewWorkspaceId),
|
||||
staticTabId: atom(PreviewTabId),
|
||||
},
|
||||
settings: {
|
||||
"web:defaultsearch": "https://www.google.com/search?q={query}",
|
||||
},
|
||||
});
|
||||
}, [baseEnv]);
|
||||
|
||||
const tabModel = React.useMemo(() => getTabModelByTabId(PreviewTabId, env), [env]);
|
||||
|
||||
return (
|
||||
<WaveEnvContext.Provider value={env}>
|
||||
<TabModelContext.Provider value={tabModel}>
|
||||
<div className="flex w-full max-w-[1100px] flex-col gap-2 px-6 py-6">
|
||||
<div className="text-xs text-muted font-mono">full web block using preview mock fallback</div>
|
||||
<div className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="h-[680px]">
|
||||
<Block preview={false} nodeModel={nodeModel} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabModelContext.Provider>
|
||||
</WaveEnvContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function WebPreview() {
|
||||
return <WebPreviewInner />;
|
||||
const nodeModel = React.useMemo(
|
||||
() => makeMockNodeModel({ nodeId: PreviewNodeId, blockId: WebBlockId, innerRect: { width: "1040px", height: "620px" } }),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-[1100px] flex-col gap-2 px-6 py-6">
|
||||
<div className="text-xs text-muted font-mono">full web block using preview mock fallback</div>
|
||||
<div className="rounded-md border border-border bg-panel p-4">
|
||||
<div className="h-[680px]">
|
||||
<Block preview={false} nodeModel={nodeModel} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue