Skip to content

Commit 96c2526

Browse files
authored
First Cut at a new ProcessViewer Widget (#3137)
1 parent e4e77e7 commit 96c2526

File tree

20 files changed

+2137
-65
lines changed

20 files changed

+2137
-65
lines changed

.kilocode/skills/create-view/SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,11 @@ export const MyView: React.FC<ViewComponentProps<MyViewModel>> = ({
203203

204204
### 3. Register the View
205205

206-
Add your view to the `BlockRegistry` in `frontend/app/block/block.tsx`:
206+
Add your view to the `BlockRegistry` in `frontend/app/block/blockregistry.ts`:
207207

208208
```typescript
209+
import { MyViewModel } from "@/app/view/myview/myview-model";
210+
209211
const BlockRegistry: Map<string, ViewModelClass> = new Map();
210212
BlockRegistry.set("term", TermViewModel);
211213
BlockRegistry.set("preview", PreviewModel);

frontend/app/block/block.tsx

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,13 @@
33

44
import {
55
BlockComponentModel2,
6-
BlockNodeModel,
76
BlockProps,
87
FullBlockProps,
98
FullSubBlockProps,
109
SubBlockProps,
1110
} from "@/app/block/blocktypes";
12-
import type { TabModel } from "@/app/store/tab-model";
1311
import { useTabModel } from "@/app/store/tab-model";
14-
import { AiFileDiffViewModel } from "@/app/view/aifilediff/aifilediff";
15-
import { LauncherViewModel } from "@/app/view/launcher/launcher";
16-
import { PreviewModel } from "@/app/view/preview/preview-model";
17-
import { SysinfoViewModel } from "@/app/view/sysinfo/sysinfo";
18-
import { TsunamiViewModel } from "@/app/view/tsunami/tsunami";
19-
import { VDomModel } from "@/app/view/vdom/vdom-model";
20-
import { useWaveEnv, WaveEnv } from "@/app/waveenv/waveenv";
12+
import { useWaveEnv } from "@/app/waveenv/waveenv";
2113
import { ErrorBoundary } from "@/element/errorboundary";
2214
import { CenteredDiv } from "@/element/quickelems";
2315
import { useDebouncedNodeInnerRect } from "@/layout/index";
@@ -26,48 +18,13 @@ import { getBlockComponentModel, registerBlockComponentModel, unregisterBlockCom
2618
import { makeORef } from "@/store/wos";
2719
import { focusedBlockId, getElemAsStr } from "@/util/focusutil";
2820
import { isBlank, useAtomValueSafe } from "@/util/util";
29-
import { HelpViewModel } from "@/view/helpview/helpview";
30-
import { TermViewModel } from "@/view/term/term-model";
31-
import { WaveAiModel } from "@/view/waveai/waveai";
32-
import { WebViewModel } from "@/view/webview/webview";
3321
import clsx from "clsx";
34-
import { atom, useAtomValue } from "jotai";
22+
import { useAtomValue } from "jotai";
3523
import { memo, Suspense, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
36-
import { QuickTipsViewModel } from "../view/quicktipsview/quicktipsview";
37-
import { WaveConfigViewModel } from "../view/waveconfig/waveconfig-model";
3824
import "./block.scss";
3925
import { BlockEnv } from "./blockenv";
4026
import { BlockFrame } from "./blockframe";
41-
import { blockViewToIcon, blockViewToName } from "./blockutil";
42-
43-
const BlockRegistry: Map<string, ViewModelClass> = new Map();
44-
BlockRegistry.set("term", TermViewModel);
45-
BlockRegistry.set("preview", PreviewModel);
46-
BlockRegistry.set("web", WebViewModel);
47-
BlockRegistry.set("waveai", WaveAiModel);
48-
BlockRegistry.set("cpuplot", SysinfoViewModel);
49-
BlockRegistry.set("sysinfo", SysinfoViewModel);
50-
BlockRegistry.set("vdom", VDomModel);
51-
BlockRegistry.set("tips", QuickTipsViewModel);
52-
BlockRegistry.set("help", HelpViewModel);
53-
BlockRegistry.set("launcher", LauncherViewModel);
54-
BlockRegistry.set("tsunami", TsunamiViewModel);
55-
BlockRegistry.set("aifilediff", AiFileDiffViewModel);
56-
BlockRegistry.set("waveconfig", WaveConfigViewModel);
57-
58-
function makeViewModel(
59-
blockId: string,
60-
blockView: string,
61-
nodeModel: BlockNodeModel,
62-
tabModel: TabModel,
63-
waveEnv: WaveEnv
64-
): ViewModel {
65-
const ctor = BlockRegistry.get(blockView);
66-
if (ctor != null) {
67-
return new ctor({ blockId, nodeModel, tabModel, waveEnv });
68-
}
69-
return makeDefaultViewModel(blockView);
70-
}
27+
import { makeViewModel } from "./blockregistry";
7128

7229
function getViewElem(
7330
blockId: string,
@@ -86,18 +43,6 @@ function getViewElem(
8643
return <VC key={blockId} blockId={blockId} blockRef={blockRef} contentRef={contentRef} model={viewModel} />;
8744
}
8845

89-
function makeDefaultViewModel(viewType: string): ViewModel {
90-
const viewModel: ViewModel = {
91-
viewType: viewType,
92-
viewIcon: atom(blockViewToIcon(viewType)),
93-
viewName: atom(blockViewToName(viewType)),
94-
preIconButton: atom(null),
95-
endIconButtons: atom(null),
96-
viewComponent: null,
97-
};
98-
return viewModel;
99-
}
100-
10146
const BlockPreview = memo(({ nodeModel, viewModel }: FullBlockProps) => {
10247
const waveEnv = useWaveEnv<BlockEnv>();
10348
const blockIsNull = useAtomValue(waveEnv.wos.isWaveObjectNullAtom(makeORef("block", nodeModel.blockId)));
@@ -250,8 +195,7 @@ const BlockFull = memo(({ nodeModel, viewModel }: FullBlockProps) => {
250195
const focusFromPointerEnter = useCallback(
251196
(event: React.PointerEvent<HTMLDivElement>) => {
252197
const focusFollowsCursorEnabled =
253-
focusFollowsCursorMode === "on" ||
254-
(focusFollowsCursorMode === "term" && blockView === "term");
198+
focusFollowsCursorMode === "on" || (focusFollowsCursorMode === "term" && blockView === "term");
255199
if (!focusFollowsCursorEnabled || event.pointerType === "touch" || event.buttons > 0) {
256200
return;
257201
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2026, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { BlockNodeModel } from "@/app/block/blocktypes";
5+
import type { TabModel } from "@/app/store/tab-model";
6+
import { AiFileDiffViewModel } from "@/app/view/aifilediff/aifilediff";
7+
import { LauncherViewModel } from "@/app/view/launcher/launcher";
8+
import { PreviewModel } from "@/app/view/preview/preview-model";
9+
import { ProcessViewerViewModel } from "@/app/view/processviewer/processviewer";
10+
import { SysinfoViewModel } from "@/app/view/sysinfo/sysinfo";
11+
import { TsunamiViewModel } from "@/app/view/tsunami/tsunami";
12+
import { VDomModel } from "@/app/view/vdom/vdom-model";
13+
import { WaveEnv } from "@/app/waveenv/waveenv";
14+
import { atom } from "jotai";
15+
import { QuickTipsViewModel } from "../view/quicktipsview/quicktipsview";
16+
import { WaveConfigViewModel } from "../view/waveconfig/waveconfig-model";
17+
import { blockViewToIcon, blockViewToName } from "./blockutil";
18+
import { HelpViewModel } from "@/view/helpview/helpview";
19+
import { TermViewModel } from "@/view/term/term-model";
20+
import { WaveAiModel } from "@/view/waveai/waveai";
21+
import { WebViewModel } from "@/view/webview/webview";
22+
23+
const BlockRegistry: Map<string, ViewModelClass> = new Map();
24+
BlockRegistry.set("term", TermViewModel);
25+
BlockRegistry.set("preview", PreviewModel);
26+
BlockRegistry.set("web", WebViewModel);
27+
BlockRegistry.set("waveai", WaveAiModel);
28+
BlockRegistry.set("cpuplot", SysinfoViewModel);
29+
BlockRegistry.set("sysinfo", SysinfoViewModel);
30+
BlockRegistry.set("vdom", VDomModel);
31+
BlockRegistry.set("tips", QuickTipsViewModel);
32+
BlockRegistry.set("help", HelpViewModel);
33+
BlockRegistry.set("launcher", LauncherViewModel);
34+
BlockRegistry.set("tsunami", TsunamiViewModel);
35+
BlockRegistry.set("aifilediff", AiFileDiffViewModel);
36+
BlockRegistry.set("waveconfig", WaveConfigViewModel);
37+
BlockRegistry.set("processviewer", ProcessViewerViewModel);
38+
39+
function makeDefaultViewModel(viewType: string): ViewModel {
40+
const viewModel: ViewModel = {
41+
viewType: viewType,
42+
viewIcon: atom(blockViewToIcon(viewType)),
43+
viewName: atom(blockViewToName(viewType)),
44+
preIconButton: atom(null),
45+
endIconButtons: atom(null),
46+
viewComponent: null,
47+
};
48+
return viewModel;
49+
}
50+
51+
function makeViewModel(
52+
blockId: string,
53+
blockView: string,
54+
nodeModel: BlockNodeModel,
55+
tabModel: TabModel,
56+
waveEnv: WaveEnv
57+
): ViewModel {
58+
const ctor = BlockRegistry.get(blockView);
59+
if (ctor != null) {
60+
return new ctor({ blockId, nodeModel, tabModel, waveEnv });
61+
}
62+
return makeDefaultViewModel(blockView);
63+
}
64+
65+
export { makeViewModel };

frontend/app/block/blockutil.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export function blockViewToIcon(view: string): string {
4242
if (view == "tips") {
4343
return "lightbulb";
4444
}
45+
if (view == "processviewer") {
46+
return "microchip";
47+
}
4548
return "square";
4649
}
4750

@@ -67,6 +70,9 @@ export function blockViewToName(view: string): string {
6770
if (view == "tips") {
6871
return "Tips";
6972
}
73+
if (view == "processviewer") {
74+
return "Processes";
75+
}
7076
return view;
7177
}
7278

frontend/app/store/wshclientapi.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,18 @@ export class RpcApiType {
756756
return client.wshRpcCall("remotemkdir", data, opts);
757757
}
758758

759+
// command "remoteprocesslist" [call]
760+
RemoteProcessListCommand(client: WshClient, data: CommandRemoteProcessListData, opts?: RpcOpts): Promise<ProcessListResponse> {
761+
if (this.mockClient) return this.mockClient.mockWshRpcCall(client, "remoteprocesslist", data, opts);
762+
return client.wshRpcCall("remoteprocesslist", data, opts);
763+
}
764+
765+
// command "remoteprocesssignal" [call]
766+
RemoteProcessSignalCommand(client: WshClient, data: CommandRemoteProcessSignalData, opts?: RpcOpts): Promise<void> {
767+
if (this.mockClient) return this.mockClient.mockWshRpcCall(client, "remoteprocesssignal", data, opts);
768+
return client.wshRpcCall("remoteprocesssignal", data, opts);
769+
}
770+
759771
// command "remotereconnecttojobmanager" [call]
760772
RemoteReconnectToJobManagerCommand(client: WshClient, data: CommandRemoteReconnectToJobManagerData, opts?: RpcOpts): Promise<CommandRemoteReconnectToJobManagerRtnData> {
761773
if (this.mockClient) return this.mockClient.mockWshRpcCall(client, "remotereconnecttojobmanager", data, opts);

0 commit comments

Comments
 (0)