@wong2kim/wmux 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +209 -157
- package/dist/cli/cli/client.js +1 -1
- package/dist/cli/cli/commands/browser.js +19 -19
- package/dist/cli/cli/index.js +58 -58
- package/dist/cli/shared/constants.js +17 -4
- package/dist/mcp/shared/constants.js +17 -4
- package/package.json +96 -84
- package/assets/icon.ico +0 -0
- package/assets/icon.svg +0 -6
- package/forge.config.ts +0 -61
- package/index.html +0 -12
- package/postcss.config.js +0 -6
- package/src/cli/client.ts +0 -76
- package/src/cli/commands/browser.ts +0 -128
- package/src/cli/commands/input.ts +0 -72
- package/src/cli/commands/notify.ts +0 -29
- package/src/cli/commands/pane.ts +0 -90
- package/src/cli/commands/surface.ts +0 -102
- package/src/cli/commands/system.ts +0 -95
- package/src/cli/commands/workspace.ts +0 -116
- package/src/cli/index.ts +0 -145
- package/src/cli/utils.ts +0 -44
- package/src/main/index.ts +0 -86
- package/src/main/ipc/handlers/clipboard.handler.ts +0 -20
- package/src/main/ipc/handlers/metadata.handler.ts +0 -56
- package/src/main/ipc/handlers/pty.handler.ts +0 -69
- package/src/main/ipc/handlers/session.handler.ts +0 -17
- package/src/main/ipc/handlers/shell.handler.ts +0 -11
- package/src/main/ipc/registerHandlers.ts +0 -31
- package/src/main/mcp/McpRegistrar.ts +0 -156
- package/src/main/metadata/MetadataCollector.ts +0 -58
- package/src/main/notification/ToastManager.ts +0 -32
- package/src/main/pipe/PipeServer.ts +0 -190
- package/src/main/pipe/RpcRouter.ts +0 -46
- package/src/main/pipe/handlers/_bridge.ts +0 -40
- package/src/main/pipe/handlers/browser.rpc.ts +0 -132
- package/src/main/pipe/handlers/input.rpc.ts +0 -120
- package/src/main/pipe/handlers/meta.rpc.ts +0 -59
- package/src/main/pipe/handlers/notify.rpc.ts +0 -53
- package/src/main/pipe/handlers/pane.rpc.ts +0 -39
- package/src/main/pipe/handlers/surface.rpc.ts +0 -43
- package/src/main/pipe/handlers/system.rpc.ts +0 -36
- package/src/main/pipe/handlers/workspace.rpc.ts +0 -52
- package/src/main/pty/AgentDetector.ts +0 -247
- package/src/main/pty/OscParser.ts +0 -81
- package/src/main/pty/PTYBridge.ts +0 -88
- package/src/main/pty/PTYManager.ts +0 -104
- package/src/main/pty/ShellDetector.ts +0 -63
- package/src/main/session/SessionManager.ts +0 -53
- package/src/main/updater/AutoUpdater.ts +0 -132
- package/src/main/window/createWindow.ts +0 -71
- package/src/mcp/README.md +0 -56
- package/src/mcp/index.ts +0 -153
- package/src/mcp/wmux-client.ts +0 -127
- package/src/preload/index.ts +0 -111
- package/src/preload/preload.ts +0 -108
- package/src/renderer/App.tsx +0 -5
- package/src/renderer/components/Browser/BrowserPanel.tsx +0 -219
- package/src/renderer/components/Browser/BrowserToolbar.tsx +0 -253
- package/src/renderer/components/Company/ApprovalDialog.tsx +0 -3
- package/src/renderer/components/Company/CompanyView.tsx +0 -7
- package/src/renderer/components/Company/MessageFeedPanel.tsx +0 -3
- package/src/renderer/components/Layout/AppLayout.tsx +0 -234
- package/src/renderer/components/Notification/NotificationPanel.tsx +0 -129
- package/src/renderer/components/Palette/CommandPalette.tsx +0 -409
- package/src/renderer/components/Palette/PaletteItem.tsx +0 -55
- package/src/renderer/components/Pane/Pane.tsx +0 -122
- package/src/renderer/components/Pane/PaneContainer.tsx +0 -41
- package/src/renderer/components/Pane/SurfaceTabs.tsx +0 -46
- package/src/renderer/components/Settings/SettingsPanel.tsx +0 -886
- package/src/renderer/components/Sidebar/MiniSidebar.tsx +0 -67
- package/src/renderer/components/Sidebar/Sidebar.tsx +0 -84
- package/src/renderer/components/Sidebar/WorkspaceItem.tsx +0 -241
- package/src/renderer/components/StatusBar/StatusBar.tsx +0 -93
- package/src/renderer/components/Terminal/SearchBar.tsx +0 -126
- package/src/renderer/components/Terminal/Terminal.tsx +0 -102
- package/src/renderer/components/Terminal/ViCopyMode.tsx +0 -104
- package/src/renderer/hooks/useKeyboard.ts +0 -310
- package/src/renderer/hooks/useNotificationListener.ts +0 -80
- package/src/renderer/hooks/useNotificationSound.ts +0 -75
- package/src/renderer/hooks/useRpcBridge.ts +0 -451
- package/src/renderer/hooks/useT.ts +0 -11
- package/src/renderer/hooks/useTerminal.ts +0 -349
- package/src/renderer/hooks/useViCopyMode.ts +0 -320
- package/src/renderer/i18n/index.ts +0 -69
- package/src/renderer/i18n/locales/en.ts +0 -157
- package/src/renderer/i18n/locales/ja.ts +0 -155
- package/src/renderer/i18n/locales/ko.ts +0 -155
- package/src/renderer/i18n/locales/zh.ts +0 -155
- package/src/renderer/index.tsx +0 -6
- package/src/renderer/stores/index.ts +0 -19
- package/src/renderer/stores/slices/notificationSlice.ts +0 -56
- package/src/renderer/stores/slices/paneSlice.ts +0 -141
- package/src/renderer/stores/slices/surfaceSlice.ts +0 -122
- package/src/renderer/stores/slices/uiSlice.ts +0 -247
- package/src/renderer/stores/slices/workspaceSlice.ts +0 -120
- package/src/renderer/styles/globals.css +0 -150
- package/src/renderer/themes.ts +0 -99
- package/src/shared/constants.ts +0 -53
- package/src/shared/electron.d.ts +0 -11
- package/src/shared/rpc.ts +0 -71
- package/src/shared/types.ts +0 -176
- package/tailwind.config.js +0 -11
- package/tsconfig.cli.json +0 -24
- package/tsconfig.json +0 -21
- package/tsconfig.mcp.json +0 -25
- package/vite.main.config.ts +0 -14
- package/vite.preload.config.ts +0 -9
- package/vite.renderer.config.ts +0 -6
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { ipcMain, type BrowserWindow } from 'electron';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { IPC } from '../../../shared/constants';
|
|
4
|
-
|
|
5
|
-
type GetWindow = () => BrowserWindow | null;
|
|
6
|
-
|
|
7
|
-
const TIMEOUT_MS = 5000;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Sends a RPC command to the renderer via IPC and waits for the response.
|
|
11
|
-
* Uses a unique requestId per call so concurrent requests don't collide.
|
|
12
|
-
*/
|
|
13
|
-
export function sendToRenderer(
|
|
14
|
-
getWindow: GetWindow,
|
|
15
|
-
method: string,
|
|
16
|
-
params: Record<string, unknown> = {},
|
|
17
|
-
): Promise<unknown> {
|
|
18
|
-
return new Promise((resolve, reject) => {
|
|
19
|
-
const win = getWindow();
|
|
20
|
-
if (!win || win.isDestroyed()) {
|
|
21
|
-
reject(new Error('BrowserWindow is not available'));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const requestId = `rpc-${randomUUID()}`;
|
|
26
|
-
const responseChannel = `${IPC.RPC_RESPONSE}:${requestId}`;
|
|
27
|
-
|
|
28
|
-
const timer = setTimeout(() => {
|
|
29
|
-
ipcMain.removeAllListeners(responseChannel);
|
|
30
|
-
reject(new Error(`RPC timeout: ${method} (${TIMEOUT_MS}ms)`));
|
|
31
|
-
}, TIMEOUT_MS);
|
|
32
|
-
|
|
33
|
-
ipcMain.once(responseChannel, (_event, result: unknown) => {
|
|
34
|
-
clearTimeout(timer);
|
|
35
|
-
resolve(result);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
win.webContents.send(IPC.RPC_COMMAND, requestId, method, params);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import { sendToRenderer } from './_bridge';
|
|
4
|
-
|
|
5
|
-
type GetWindow = () => BrowserWindow | null;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Registers browser.* RPC handlers.
|
|
9
|
-
*
|
|
10
|
-
* All commands are delegated to the renderer process via IPC where the active
|
|
11
|
-
* browser Surface's <webview> element executes the requested operation.
|
|
12
|
-
*/
|
|
13
|
-
export function registerBrowserRpc(router: RpcRouter, getWindow: GetWindow): void {
|
|
14
|
-
/**
|
|
15
|
-
* browser.open
|
|
16
|
-
* Opens a new browser surface in the active pane.
|
|
17
|
-
* params: { url?: string }
|
|
18
|
-
*/
|
|
19
|
-
router.register('browser.open', (params) => {
|
|
20
|
-
const url = typeof params['url'] === 'string' ? params['url'] : undefined;
|
|
21
|
-
return sendToRenderer(getWindow, 'browser.open', {
|
|
22
|
-
...(url && { url }),
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* browser.snapshot
|
|
28
|
-
* Returns the full outer HTML of the current page as a string.
|
|
29
|
-
* params: {}
|
|
30
|
-
*/
|
|
31
|
-
router.register('browser.snapshot', (params) => {
|
|
32
|
-
const surfaceId = typeof params['surfaceId'] === 'string' ? params['surfaceId'] : undefined;
|
|
33
|
-
return sendToRenderer(getWindow, 'browser.snapshot', {
|
|
34
|
-
...(surfaceId && { surfaceId }),
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* browser.click
|
|
40
|
-
* Clicks the first element matching the given CSS selector.
|
|
41
|
-
* params: { selector: string }
|
|
42
|
-
*/
|
|
43
|
-
router.register('browser.click', (params) => {
|
|
44
|
-
if (typeof params['selector'] !== 'string' || params['selector'].length === 0) {
|
|
45
|
-
throw new Error('browser.click: missing required param "selector"');
|
|
46
|
-
}
|
|
47
|
-
const surfaceId = typeof params['surfaceId'] === 'string' ? params['surfaceId'] : undefined;
|
|
48
|
-
return sendToRenderer(getWindow, 'browser.click', {
|
|
49
|
-
selector: params['selector'],
|
|
50
|
-
...(surfaceId && { surfaceId }),
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* browser.fill
|
|
56
|
-
* Sets the value of an input element matching the given CSS selector.
|
|
57
|
-
* params: { selector: string; text: string }
|
|
58
|
-
*/
|
|
59
|
-
router.register('browser.fill', (params) => {
|
|
60
|
-
if (typeof params['selector'] !== 'string' || params['selector'].length === 0) {
|
|
61
|
-
throw new Error('browser.fill: missing required param "selector"');
|
|
62
|
-
}
|
|
63
|
-
if (typeof params['text'] !== 'string') {
|
|
64
|
-
throw new Error('browser.fill: missing required param "text"');
|
|
65
|
-
}
|
|
66
|
-
const surfaceId = typeof params['surfaceId'] === 'string' ? params['surfaceId'] : undefined;
|
|
67
|
-
return sendToRenderer(getWindow, 'browser.fill', {
|
|
68
|
-
selector: params['selector'],
|
|
69
|
-
text: params['text'],
|
|
70
|
-
...(surfaceId && { surfaceId }),
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* browser.eval
|
|
76
|
-
* Evaluates arbitrary JavaScript in the context of the current page.
|
|
77
|
-
* params: { code: string }
|
|
78
|
-
*/
|
|
79
|
-
router.register('browser.eval', (params) => {
|
|
80
|
-
if (typeof params['code'] !== 'string' || params['code'].length === 0) {
|
|
81
|
-
throw new Error('browser.eval: missing required param "code"');
|
|
82
|
-
}
|
|
83
|
-
// Security: block patterns that could escape webview sandbox
|
|
84
|
-
const code = params['code'];
|
|
85
|
-
const dangerousPatterns = [
|
|
86
|
-
/\brequire\s*\(/i,
|
|
87
|
-
/\bprocess\s*\./i,
|
|
88
|
-
/\b__dirname\b/i,
|
|
89
|
-
/\b__filename\b/i,
|
|
90
|
-
/\bchild_process\b/i,
|
|
91
|
-
/\bglobal\s*\.\s*process\b/i,
|
|
92
|
-
/\belectron\b/i,
|
|
93
|
-
];
|
|
94
|
-
for (const pat of dangerousPatterns) {
|
|
95
|
-
if (pat.test(code)) {
|
|
96
|
-
throw new Error('browser.eval: code contains blocked pattern');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
const surfaceId = typeof params['surfaceId'] === 'string' ? params['surfaceId'] : undefined;
|
|
100
|
-
return sendToRenderer(getWindow, 'browser.eval', {
|
|
101
|
-
code,
|
|
102
|
-
...(surfaceId && { surfaceId }),
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* browser.navigate
|
|
108
|
-
* Navigates the active browser Surface to the given URL.
|
|
109
|
-
* params: { url: string }
|
|
110
|
-
*/
|
|
111
|
-
router.register('browser.navigate', (params) => {
|
|
112
|
-
if (typeof params['url'] !== 'string' || params['url'].length === 0) {
|
|
113
|
-
throw new Error('browser.navigate: missing required param "url"');
|
|
114
|
-
}
|
|
115
|
-
// Security: block dangerous URL schemes
|
|
116
|
-
const url = params['url'];
|
|
117
|
-
const normalizedUrl = url.trim().toLowerCase();
|
|
118
|
-
if (
|
|
119
|
-
normalizedUrl.startsWith('javascript:') ||
|
|
120
|
-
normalizedUrl.startsWith('data:') ||
|
|
121
|
-
normalizedUrl.startsWith('vbscript:') ||
|
|
122
|
-
normalizedUrl.startsWith('file:')
|
|
123
|
-
) {
|
|
124
|
-
throw new Error(`browser.navigate: blocked URL scheme`);
|
|
125
|
-
}
|
|
126
|
-
const surfaceId = typeof params['surfaceId'] === 'string' ? params['surfaceId'] : undefined;
|
|
127
|
-
return sendToRenderer(getWindow, 'browser.navigate', {
|
|
128
|
-
url,
|
|
129
|
-
...(surfaceId && { surfaceId }),
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import type { PTYManager } from '../../pty/PTYManager';
|
|
4
|
-
import { sendToRenderer } from './_bridge';
|
|
5
|
-
|
|
6
|
-
type GetWindow = () => BrowserWindow | null;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Key sequence mapping table for input.sendKey
|
|
10
|
-
*/
|
|
11
|
-
const KEY_MAP: Readonly<Record<string, string>> = {
|
|
12
|
-
enter: '\r',
|
|
13
|
-
tab: '\t',
|
|
14
|
-
'ctrl+c': '\x03',
|
|
15
|
-
'ctrl+d': '\x04',
|
|
16
|
-
'ctrl+z': '\x1a',
|
|
17
|
-
'ctrl+l': '\x0c',
|
|
18
|
-
escape: '\x1b',
|
|
19
|
-
up: '\x1b[A',
|
|
20
|
-
down: '\x1b[B',
|
|
21
|
-
right: '\x1b[C',
|
|
22
|
-
left: '\x1b[D',
|
|
23
|
-
} as const;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Resolves the active ptyId from the renderer when none is provided.
|
|
27
|
-
* Asks the renderer for the currently focused surface's ptyId.
|
|
28
|
-
*/
|
|
29
|
-
async function resolveActivePtyId(getWindow: GetWindow): Promise<string> {
|
|
30
|
-
const result = await sendToRenderer(getWindow, 'input.readScreen');
|
|
31
|
-
// renderer returns { ptyId: string, ... } for the active surface
|
|
32
|
-
if (
|
|
33
|
-
result !== null &&
|
|
34
|
-
typeof result === 'object' &&
|
|
35
|
-
'ptyId' in result &&
|
|
36
|
-
typeof (result as Record<string, unknown>)['ptyId'] === 'string'
|
|
37
|
-
) {
|
|
38
|
-
return (result as Record<string, string>)['ptyId'];
|
|
39
|
-
}
|
|
40
|
-
throw new Error('input: could not resolve active ptyId from renderer');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function registerInputRpc(
|
|
44
|
-
router: RpcRouter,
|
|
45
|
-
ptyManager: PTYManager,
|
|
46
|
-
getWindow: GetWindow,
|
|
47
|
-
): void {
|
|
48
|
-
/**
|
|
49
|
-
* input.send — writes text to a PTY session.
|
|
50
|
-
* params: { text: string, ptyId?: string }
|
|
51
|
-
* If ptyId is omitted the renderer is queried for the active surface's ptyId.
|
|
52
|
-
*/
|
|
53
|
-
router.register('input.send', async (params) => {
|
|
54
|
-
if (typeof params['text'] !== 'string') {
|
|
55
|
-
throw new Error('input.send: missing required param "text"');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const text = params['text'];
|
|
59
|
-
let ptyId: string;
|
|
60
|
-
|
|
61
|
-
if (typeof params['ptyId'] === 'string' && params['ptyId'].length > 0) {
|
|
62
|
-
ptyId = params['ptyId'];
|
|
63
|
-
} else {
|
|
64
|
-
ptyId = await resolveActivePtyId(getWindow);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const instance = ptyManager.get(ptyId);
|
|
68
|
-
if (!instance) {
|
|
69
|
-
throw new Error(`input.send: PTY not found — id="${ptyId}"`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
ptyManager.write(ptyId, text);
|
|
73
|
-
return { ok: true, ptyId };
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* input.sendKey — maps a named key to an ANSI sequence and writes it.
|
|
78
|
-
* params: { key: string, ptyId?: string }
|
|
79
|
-
* Supported keys: enter, tab, ctrl+c, ctrl+d, ctrl+z, ctrl+l,
|
|
80
|
-
* escape, up, down, right, left
|
|
81
|
-
*/
|
|
82
|
-
router.register('input.sendKey', async (params) => {
|
|
83
|
-
if (typeof params['key'] !== 'string') {
|
|
84
|
-
throw new Error('input.sendKey: missing required param "key"');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const key = params['key'].toLowerCase();
|
|
88
|
-
const sequence = KEY_MAP[key];
|
|
89
|
-
if (sequence === undefined) {
|
|
90
|
-
throw new Error(
|
|
91
|
-
`input.sendKey: unknown key "${params['key']}". ` +
|
|
92
|
-
`Supported: ${Object.keys(KEY_MAP).join(', ')}`,
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
let ptyId: string;
|
|
97
|
-
if (typeof params['ptyId'] === 'string' && params['ptyId'].length > 0) {
|
|
98
|
-
ptyId = params['ptyId'];
|
|
99
|
-
} else {
|
|
100
|
-
ptyId = await resolveActivePtyId(getWindow);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const instance = ptyManager.get(ptyId);
|
|
104
|
-
if (!instance) {
|
|
105
|
-
throw new Error(`input.sendKey: PTY not found — id="${ptyId}"`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
ptyManager.write(ptyId, sequence);
|
|
109
|
-
return { ok: true, ptyId, key, sequence };
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* input.readScreen — delegates to the renderer to capture the current
|
|
114
|
-
* terminal viewport text of the active surface.
|
|
115
|
-
* Returns { ptyId: string, text: string }
|
|
116
|
-
*/
|
|
117
|
-
router.register('input.readScreen', (_params) =>
|
|
118
|
-
sendToRenderer(getWindow, 'input.readScreen'),
|
|
119
|
-
);
|
|
120
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import { IPC } from '../../../shared/constants';
|
|
4
|
-
|
|
5
|
-
type GetWindow = () => BrowserWindow | null;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Sub-channel names embedded in the METADATA_UPDATE IPC message.
|
|
9
|
-
* The renderer's useNotificationListener (or a dedicated metadata listener)
|
|
10
|
-
* discriminates on the `kind` field.
|
|
11
|
-
*/
|
|
12
|
-
type MetaUpdateKind = 'status' | 'progress';
|
|
13
|
-
|
|
14
|
-
interface MetaStatusPayload {
|
|
15
|
-
kind: 'status';
|
|
16
|
-
text: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface MetaProgressPayload {
|
|
20
|
-
kind: 'progress';
|
|
21
|
-
value: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
type MetaPayload = MetaStatusPayload | MetaProgressPayload;
|
|
25
|
-
|
|
26
|
-
function sendMeta(getWindow: GetWindow, payload: MetaPayload): Promise<{ ok: boolean }> {
|
|
27
|
-
const win = getWindow();
|
|
28
|
-
if (!win || win.isDestroyed()) {
|
|
29
|
-
return Promise.reject(new Error('meta: BrowserWindow is not available'));
|
|
30
|
-
}
|
|
31
|
-
win.webContents.send(IPC.METADATA_UPDATE, payload);
|
|
32
|
-
return Promise.resolve({ ok: true });
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function registerMetaRpc(router: RpcRouter, getWindow: GetWindow): void {
|
|
36
|
-
/**
|
|
37
|
-
* meta.setStatus — sets an arbitrary status text string in the renderer.
|
|
38
|
-
* params: { text: string }
|
|
39
|
-
*/
|
|
40
|
-
router.register('meta.setStatus', (params) => {
|
|
41
|
-
if (typeof params['text'] !== 'string') {
|
|
42
|
-
throw new Error('meta.setStatus: missing required param "text"');
|
|
43
|
-
}
|
|
44
|
-
return sendMeta(getWindow, { kind: 'status', text: params['text'] });
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* meta.setProgress — sets a progress value (0–100) in the renderer.
|
|
49
|
-
* params: { value: number }
|
|
50
|
-
* Values outside 0–100 are clamped.
|
|
51
|
-
*/
|
|
52
|
-
router.register('meta.setProgress', (params) => {
|
|
53
|
-
if (typeof params['value'] !== 'number') {
|
|
54
|
-
throw new Error('meta.setProgress: missing required param "value" (number)');
|
|
55
|
-
}
|
|
56
|
-
const value = Math.min(100, Math.max(0, params['value']));
|
|
57
|
-
return sendMeta(getWindow, { kind: 'progress', value });
|
|
58
|
-
});
|
|
59
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import type { NotificationType } from '../../../shared/types';
|
|
4
|
-
import { IPC } from '../../../shared/constants';
|
|
5
|
-
import { ToastManager } from '../../notification/ToastManager';
|
|
6
|
-
|
|
7
|
-
type GetWindow = () => BrowserWindow | null;
|
|
8
|
-
|
|
9
|
-
const VALID_TYPES = new Set<NotificationType>(['info', 'warning', 'error', 'agent']);
|
|
10
|
-
|
|
11
|
-
function isNotificationType(value: unknown): value is NotificationType {
|
|
12
|
-
return typeof value === 'string' && VALID_TYPES.has(value as NotificationType);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const toastManager = new ToastManager();
|
|
16
|
-
|
|
17
|
-
export function registerNotifyRpc(router: RpcRouter, getWindow: GetWindow): void {
|
|
18
|
-
/**
|
|
19
|
-
* notify — delivers a notification to the renderer UI and, when the app is
|
|
20
|
-
* not focused, also shows a Windows Toast notification.
|
|
21
|
-
*
|
|
22
|
-
* params: {
|
|
23
|
-
* title: string
|
|
24
|
-
* body: string
|
|
25
|
-
* type?: 'info' | 'warning' | 'error' | 'agent' (default: 'info')
|
|
26
|
-
* }
|
|
27
|
-
*/
|
|
28
|
-
router.register('notify', (params) => {
|
|
29
|
-
if (typeof params['title'] !== 'string' || params['title'].length === 0) {
|
|
30
|
-
throw new Error('notify: missing required param "title"');
|
|
31
|
-
}
|
|
32
|
-
if (typeof params['body'] !== 'string') {
|
|
33
|
-
throw new Error('notify: missing required param "body"');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const title = params['title'];
|
|
37
|
-
const body = params['body'];
|
|
38
|
-
const type: NotificationType = isNotificationType(params['type'])
|
|
39
|
-
? params['type']
|
|
40
|
-
: 'info';
|
|
41
|
-
|
|
42
|
-
const win = getWindow();
|
|
43
|
-
if (win && !win.isDestroyed()) {
|
|
44
|
-
// Push notification to the renderer notification store
|
|
45
|
-
win.webContents.send(IPC.NOTIFICATION, { title, body, type });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Show OS-level toast (only when window is not focused)
|
|
49
|
-
toastManager.show(title, body);
|
|
50
|
-
|
|
51
|
-
return Promise.resolve({ delivered: true, type });
|
|
52
|
-
});
|
|
53
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import { sendToRenderer } from './_bridge';
|
|
4
|
-
|
|
5
|
-
type GetWindow = () => BrowserWindow | null;
|
|
6
|
-
|
|
7
|
-
export function registerPaneRpc(router: RpcRouter, getWindow: GetWindow): void {
|
|
8
|
-
/**
|
|
9
|
-
* pane.list — returns all panes (leaf nodes) of the current workspace
|
|
10
|
-
*/
|
|
11
|
-
router.register('pane.list', (_params) =>
|
|
12
|
-
sendToRenderer(getWindow, 'pane.list'),
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* pane.focus — focuses a specific pane
|
|
17
|
-
* params: { id: string }
|
|
18
|
-
*/
|
|
19
|
-
router.register('pane.focus', (params) => {
|
|
20
|
-
if (typeof params['id'] !== 'string') {
|
|
21
|
-
return Promise.reject(new Error('pane.focus: missing required param "id"'));
|
|
22
|
-
}
|
|
23
|
-
return sendToRenderer(getWindow, 'pane.focus', { id: params['id'] });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* pane.split — splits the active pane
|
|
28
|
-
* params: { direction: 'horizontal' | 'vertical' }
|
|
29
|
-
*/
|
|
30
|
-
router.register('pane.split', (params) => {
|
|
31
|
-
const direction = params['direction'];
|
|
32
|
-
if (direction !== 'horizontal' && direction !== 'vertical') {
|
|
33
|
-
return Promise.reject(
|
|
34
|
-
new Error('pane.split: "direction" must be "horizontal" or "vertical"'),
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
return sendToRenderer(getWindow, 'pane.split', { direction });
|
|
38
|
-
});
|
|
39
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import { sendToRenderer } from './_bridge';
|
|
4
|
-
|
|
5
|
-
type GetWindow = () => BrowserWindow | null;
|
|
6
|
-
|
|
7
|
-
export function registerSurfaceRpc(router: RpcRouter, getWindow: GetWindow): void {
|
|
8
|
-
/**
|
|
9
|
-
* surface.list — returns surfaces of the current workspace's active pane
|
|
10
|
-
*/
|
|
11
|
-
router.register('surface.list', (_params) =>
|
|
12
|
-
sendToRenderer(getWindow, 'surface.list'),
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* surface.new — creates a new surface in the active pane
|
|
17
|
-
*/
|
|
18
|
-
router.register('surface.new', (_params) =>
|
|
19
|
-
sendToRenderer(getWindow, 'surface.new'),
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* surface.focus — focuses a specific surface
|
|
24
|
-
* params: { id: string }
|
|
25
|
-
*/
|
|
26
|
-
router.register('surface.focus', (params) => {
|
|
27
|
-
if (typeof params['id'] !== 'string') {
|
|
28
|
-
return Promise.reject(new Error('surface.focus: missing required param "id"'));
|
|
29
|
-
}
|
|
30
|
-
return sendToRenderer(getWindow, 'surface.focus', { id: params['id'] });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* surface.close — closes a specific surface
|
|
35
|
-
* params: { id: string }
|
|
36
|
-
*/
|
|
37
|
-
router.register('surface.close', (params) => {
|
|
38
|
-
if (typeof params['id'] !== 'string') {
|
|
39
|
-
return Promise.reject(new Error('surface.close: missing required param "id"'));
|
|
40
|
-
}
|
|
41
|
-
return sendToRenderer(getWindow, 'surface.close', { id: params['id'] });
|
|
42
|
-
});
|
|
43
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { app } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import { ALL_RPC_METHODS } from '../../../shared/rpc';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Shape returned by system.identify.
|
|
7
|
-
*/
|
|
8
|
-
interface SystemIdentity {
|
|
9
|
-
app: string;
|
|
10
|
-
version: string;
|
|
11
|
-
platform: NodeJS.Platform;
|
|
12
|
-
electronVersion: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function registerSystemRpc(router: RpcRouter): void {
|
|
16
|
-
/**
|
|
17
|
-
* system.identify — returns static information about the running WinMux instance.
|
|
18
|
-
* No renderer round-trip needed; answered entirely from Main.
|
|
19
|
-
*/
|
|
20
|
-
router.register('system.identify', (_params): Promise<SystemIdentity> => {
|
|
21
|
-
return Promise.resolve({
|
|
22
|
-
app: 'wmux',
|
|
23
|
-
version: app.getVersion(),
|
|
24
|
-
platform: process.platform,
|
|
25
|
-
electronVersion: process.versions.electron ?? 'unknown',
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* system.capabilities — returns the full list of registered RPC method names.
|
|
31
|
-
* Sourced from the single-source-of-truth array in shared/rpc.ts.
|
|
32
|
-
*/
|
|
33
|
-
router.register('system.capabilities', (_params) => {
|
|
34
|
-
return Promise.resolve({ methods: ALL_RPC_METHODS });
|
|
35
|
-
});
|
|
36
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import type { BrowserWindow } from 'electron';
|
|
2
|
-
import type { RpcRouter } from '../RpcRouter';
|
|
3
|
-
import { sendToRenderer } from './_bridge';
|
|
4
|
-
|
|
5
|
-
type GetWindow = () => BrowserWindow | null;
|
|
6
|
-
|
|
7
|
-
export function registerWorkspaceRpc(router: RpcRouter, getWindow: GetWindow): void {
|
|
8
|
-
/**
|
|
9
|
-
* workspace.list — returns all workspaces as {id, name}[]
|
|
10
|
-
*/
|
|
11
|
-
router.register('workspace.list', (_params) =>
|
|
12
|
-
sendToRenderer(getWindow, 'workspace.list'),
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* workspace.new — creates a new workspace
|
|
17
|
-
* params: { name?: string }
|
|
18
|
-
*/
|
|
19
|
-
router.register('workspace.new', (params) => {
|
|
20
|
-
const name = typeof params['name'] === 'string' ? params['name'] : undefined;
|
|
21
|
-
return sendToRenderer(getWindow, 'workspace.new', name !== undefined ? { name } : {});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* workspace.focus — sets the active workspace
|
|
26
|
-
* params: { id: string }
|
|
27
|
-
*/
|
|
28
|
-
router.register('workspace.focus', (params) => {
|
|
29
|
-
if (typeof params['id'] !== 'string') {
|
|
30
|
-
return Promise.reject(new Error('workspace.focus: missing required param "id"'));
|
|
31
|
-
}
|
|
32
|
-
return sendToRenderer(getWindow, 'workspace.focus', { id: params['id'] });
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* workspace.close — removes a workspace
|
|
37
|
-
* params: { id: string }
|
|
38
|
-
*/
|
|
39
|
-
router.register('workspace.close', (params) => {
|
|
40
|
-
if (typeof params['id'] !== 'string') {
|
|
41
|
-
return Promise.reject(new Error('workspace.close: missing required param "id"'));
|
|
42
|
-
}
|
|
43
|
-
return sendToRenderer(getWindow, 'workspace.close', { id: params['id'] });
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* workspace.current — returns the currently active workspace {id, name}
|
|
48
|
-
*/
|
|
49
|
-
router.register('workspace.current', (_params) =>
|
|
50
|
-
sendToRenderer(getWindow, 'workspace.current'),
|
|
51
|
-
);
|
|
52
|
-
}
|