From 9e11f38cf9ab53cc7a74bc24b7de328ce9df0229 Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 11 Mar 2026 22:10:47 -0700 Subject: [PATCH 1/2] fix contextmenu for block in mocks --- frontend/app/block/blockenv.ts | 1 + frontend/app/block/blockframe-header.tsx | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/app/block/blockenv.ts b/frontend/app/block/blockenv.ts index b2df51192d..000228c014 100644 --- a/frontend/app/block/blockenv.ts +++ b/frontend/app/block/blockenv.ts @@ -16,6 +16,7 @@ export type BlockEnv = WaveEnvSubset<{ | "window:magnifiedblockblurprimarypx" | "window:magnifiedblockopacity" >; + showContextMenu: WaveEnv["showContextMenu"]; atoms: { modalOpen: WaveEnv["atoms"]["modalOpen"]; controlShiftDelayAtom: WaveEnv["atoms"]["controlShiftDelayAtom"]; diff --git a/frontend/app/block/blockframe-header.tsx b/frontend/app/block/blockframe-header.tsx index 252f1f8845..319e9b4a49 100644 --- a/frontend/app/block/blockframe-header.tsx +++ b/frontend/app/block/blockframe-header.tsx @@ -11,26 +11,26 @@ import { import { ConnectionButton } from "@/app/block/connectionbutton"; import { DurableSessionFlyover } from "@/app/block/durable-session-flyover"; import { getBlockBadgeAtom } from "@/app/store/badge"; -import { ContextMenuModel } from "@/app/store/contextmenu"; import { recordTEvent, refocusNode } from "@/app/store/global"; import { globalStore } from "@/app/store/jotaiStore"; import { uxCloseBlock } from "@/app/store/keymodel"; import { TabRpcClient } from "@/app/store/wshrpcutil"; import { useWaveEnv } from "@/app/waveenv/waveenv"; -import { BlockEnv } from "./blockenv"; import { IconButton } from "@/element/iconbutton"; import { NodeModel } from "@/layout/index"; import * as util from "@/util/util"; import { cn, makeIconClass } from "@/util/util"; import * as jotai from "jotai"; import * as React from "react"; +import { BlockEnv } from "./blockenv"; import { BlockFrameProps } from "./blocktypes"; function handleHeaderContextMenu( e: React.MouseEvent, blockId: string, viewModel: ViewModel, - nodeModel: NodeModel + nodeModel: NodeModel, + blockEnv: BlockEnv ) { e.preventDefault(); e.stopPropagation(); @@ -59,7 +59,7 @@ function handleHeaderContextMenu( click: () => uxCloseBlock(blockId), } ); - ContextMenuModel.getInstance().showContextMenu(menu, e); + blockEnv.showContextMenu(menu, e); } type HeaderTextElemsProps = { @@ -113,6 +113,7 @@ type HeaderEndIconsProps = { }; const HeaderEndIcons = React.memo(({ viewModel, nodeModel, blockId }: HeaderEndIconsProps) => { + const blockEnv = useWaveEnv(); const endIconButtons = util.useAtomValueSafe(viewModel?.endIconButtons); const magnified = jotai.useAtomValue(nodeModel.isMagnified); const ephemeral = jotai.useAtomValue(nodeModel.isEphemeral); @@ -128,7 +129,7 @@ const HeaderEndIcons = React.memo(({ viewModel, nodeModel, blockId }: HeaderEndI elemtype: "iconbutton", icon: "cog", title: "Settings", - click: (e) => handleHeaderContextMenu(e, blockId, viewModel, nodeModel), + click: (e) => handleHeaderContextMenu(e, blockId, viewModel, nodeModel, blockEnv), }; endIconsElem.push(); if (ephemeral) { @@ -211,7 +212,7 @@ const BlockFrame_Header = ({ className={cn("block-frame-default-header", useTermHeader && "!pl-[2px]")} data-role="block-header" ref={dragHandleRef} - onContextMenu={(e) => handleHeaderContextMenu(e, nodeModel.blockId, viewModel, nodeModel)} + onContextMenu={(e) => handleHeaderContextMenu(e, nodeModel.blockId, viewModel, nodeModel, waveEnv)} > {!useTermHeader && ( <> From 7282e08524676cdc8da578f1b4adbf270cd9e597 Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 11 Mar 2026 22:13:18 -0700 Subject: [PATCH 2/2] add setconfig mock --- frontend/preview/mock/mockwaveenv.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frontend/preview/mock/mockwaveenv.ts b/frontend/preview/mock/mockwaveenv.ts index edf2fe4771..7342b49939 100644 --- a/frontend/preview/mock/mockwaveenv.ts +++ b/frontend/preview/mock/mockwaveenv.ts @@ -20,6 +20,7 @@ import { previewElectronApi } from "./preview-electron-api"; // is purely FE-based (registered via WPS on the frontend) // - rpc.GetMetaCommand -- reads .meta from the mock WOS atom for the given oref // - rpc.SetMetaCommand -- writes .meta to the mock WOS atom (null values delete keys) +// - rpc.SetConfigCommand -- merges settings into fullConfigAtom (null values delete keys) // - rpc.UpdateTabNameCommand -- updates .name on the Tab WaveObj in the mock WOS // - rpc.UpdateWorkspaceTabIdsCommand -- updates .tabids on the Workspace WaveObj in the mock WOS // @@ -173,6 +174,7 @@ function makeMockGlobalAtoms( type MockWosFns = { getWaveObjectAtom: (oref: string) => PrimitiveAtom; mockSetWaveObj: (oref: string, obj: T) => void; + fullConfigAtom: PrimitiveAtom; }; export function makeMockRpc(overrides: RpcOverrides, wos: MockWosFns): RpcApiType { @@ -211,6 +213,19 @@ export function makeMockRpc(overrides: RpcOverrides, wos: MockWosFns): RpcApiTyp wos.mockSetWaveObj(tabORef, updated); return null; }); + dispatchMap.set("setconfig", async (_client, data: SettingsType) => { + const current = globalStore.get(wos.fullConfigAtom); + const updatedSettings = { ...(current?.settings ?? {}) }; + for (const [key, value] of Object.entries(data)) { + if (value === null) { + delete (updatedSettings as any)[key]; + } else { + (updatedSettings as any)[key] = value; + } + } + globalStore.set(wos.fullConfigAtom, { ...current, settings: updatedSettings as SettingsType }); + return null; + }); dispatchMap.set("updateworkspacetabids", async (_client, data: { args: [string, string[]] }) => { const [workspaceId, tabIds] = data.args; const wsORef = "workspace:" + workspaceId; @@ -280,6 +295,7 @@ export function makeMockWaveEnv(mockEnv?: MockEnv): MockWaveEnv { }); const mockWosFns: MockWosFns = { getWaveObjectAtom, + fullConfigAtom: atoms.fullConfigAtom, mockSetWaveObj: (oref: string, obj: T) => { if (!waveObjectValueAtomCache.has(oref)) { waveObjectValueAtomCache.set(oref, atom(null as WaveObj));