Skip to content

Commit e0ea3fe

Browse files
committed
keybinding finalized
1 parent bb8dffc commit e0ea3fe

File tree

12 files changed

+86
-834
lines changed

12 files changed

+86
-834
lines changed

.claude/settings.local.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(npx tsc:*)",
5+
"Bash(git:*)",
6+
"Bash(grep -E \"\\\\.\\(ts|tsx|json|go\\)$\")",
7+
"Bash(find /Users/mswiszcz/Labs/waveterm/docs -name *.md)",
8+
"Bash(find /Users/mswiszcz/Labs/waveterm/frontend -name *vtab* -type f)",
9+
"Bash(/Users/mswiszcz/.claude/plugins/cache/claude-plugins-official/superpowers/5.0.6/skills/brainstorming/scripts/start-server.sh --project-dir /Users/mswiszcz/Labs/waveterm)",
10+
"Bash(grep -r \"registerKeybinding\\\\|registerShortcut\\\\|keybinding.*config\" /Users/mswiszcz/Labs/waveterm --include=*.ts --include=*.tsx)",
11+
"Bash(gh api:*)",
12+
"Bash(python3 -c \"import sys,json; data=json.load\\(sys.stdin\\); [print\\(x[''''path'''']\\) for x in data.get\\(''''tree'''',[]\\) if any\\(k in x[''''path''''].lower\\(\\) for k in [''''color'''',''''palette'''',''''theme'''',''''ansi'''']\\)]\")",
13+
"WebFetch(domain:github.com)",
14+
"Bash(gh repo:*)",
15+
"WebSearch",
16+
"WebFetch(domain:docs.warp.dev)",
17+
"Bash(gh search:*)",
18+
"Bash(grep -E \"\\\\.\\(ts|tsx\\)$\")",
19+
"Bash(grep -E \"\\\\.\\(ts|tsx|json\\)$\")",
20+
"Bash(/Users/mswiszcz/.claude/plugins/cache/claude-plugins-official/superpowers/5.0.6/scripts/start-server.sh --project-dir /Users/mswiszcz/Labs/waveterm)",
21+
"Bash(ls /Users/mswiszcz/Labs/waveterm/cmd/generate*/)",
22+
"Bash(bash /Users/mswiszcz/.claude/plugins/cache/claude-plugins-official/superpowers/5.0.6/skills/brainstorming/scripts/start-server.sh --project-dir /Users/mswiszcz/Labs/waveterm)",
23+
"Bash(go run:*)",
24+
"Read(//Users/mswiszcz/.claude/**)",
25+
"Bash(ls /Users/mswiszcz/.claude/projects/-Users-mswiszcz-Labs-waveterm/settings*.json)"
26+
]
27+
}
28+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@ test-results.xml
3939
docsite/
4040

4141
.kilo-format-temp-*
42+
.superpowers
43+
docs/superpowers

docs/superpowers/specs/2026-03-27-keybindings-config-design.md

Lines changed: 0 additions & 154 deletions
This file was deleted.

emain/emain-ipc.ts

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { PNG } from "pngjs";
1010
import { Readable } from "stream";
1111
import { RpcApi } from "../frontend/app/store/wshclientapi";
1212
import { getWebServerEndpoint } from "../frontend/util/endpoints";
13-
import * as keyutil from "../frontend/util/keyutil";
1413
import { fireAndForget, parseDataUrl } from "../frontend/util/util";
1514
import {
1615
incrementTermCommandsDurable,
@@ -22,16 +21,13 @@ import {
2221
import { createBuilderWindow, getAllBuilderWindows, getBuilderWindowByWebContentsId } from "./emain-builder";
2322
import { callWithOriginalXdgCurrentDesktopAsync, unamePlatform } from "./emain-platform";
2423
import { getWaveTabViewByWebContentsId } from "./emain-tabview";
25-
import { handleCtrlShiftState } from "./emain-util";
24+
import { setWebviewKeys } from "./emain-util";
2625
import { getWaveVersion } from "./emain-wavesrv";
2726
import { createNewWaveWindow, getWaveWindowByWebContentsId } from "./emain-window";
2827
import { ElectronWshClient } from "./emain-wsh";
2928

3029
const electronApp = electron.app;
3130

32-
let webviewFocusId: number = null;
33-
let webviewKeys: string[] = [];
34-
3531
export function openBuilderWindow(appId?: string) {
3632
const normalizedAppId = appId || "";
3733
const existingBuilderWindows = getAllBuilderWindows();
@@ -236,6 +232,14 @@ export function initIpcHandlers() {
236232
menu.popup();
237233
});
238234

235+
electron.ipcMain.on("webview-mouse-navigate", (event: electron.IpcMainEvent, direction: string) => {
236+
if (direction === "back") {
237+
event.sender.goBack();
238+
} else if (direction === "forward") {
239+
event.sender.goForward();
240+
}
241+
});
242+
239243
electron.ipcMain.on("download", (event, payload) => {
240244
const baseName = encodeURIComponent(path.basename(payload.filePath));
241245
const streamingUrl =
@@ -280,48 +284,12 @@ export function initIpcHandlers() {
280284
event.returnValue = event.sender.getZoomFactor();
281285
});
282286

283-
const hasBeforeInputRegisteredMap = new Map<number, boolean>();
284-
285-
electron.ipcMain.on("webview-focus", (event: Electron.IpcMainEvent, focusedId: number) => {
286-
webviewFocusId = focusedId;
287+
electron.ipcMain.on("webview-focus", (_event: Electron.IpcMainEvent, focusedId: number) => {
287288
console.log("webview-focus", focusedId);
288-
if (focusedId == null) {
289-
return;
290-
}
291-
const parentWc = event.sender;
292-
const webviewWc = electron.webContents.fromId(focusedId);
293-
if (webviewWc == null) {
294-
webviewFocusId = null;
295-
return;
296-
}
297-
if (!hasBeforeInputRegisteredMap.get(focusedId)) {
298-
hasBeforeInputRegisteredMap.set(focusedId, true);
299-
webviewWc.on("before-input-event", (e, input) => {
300-
let waveEvent = keyutil.adaptFromElectronKeyEvent(input);
301-
handleCtrlShiftState(parentWc, waveEvent);
302-
if (webviewFocusId != focusedId) {
303-
return;
304-
}
305-
if (input.type != "keyDown") {
306-
return;
307-
}
308-
for (let keyDesc of webviewKeys) {
309-
if (keyutil.checkKeyPressed(waveEvent, keyDesc)) {
310-
e.preventDefault();
311-
parentWc.send("reinject-key", waveEvent);
312-
console.log("webview reinject-key", keyDesc);
313-
return;
314-
}
315-
}
316-
});
317-
webviewWc.on("destroyed", () => {
318-
hasBeforeInputRegisteredMap.delete(focusedId);
319-
});
320-
}
321289
});
322290

323291
electron.ipcMain.on("register-global-webview-keys", (event, keys: string[]) => {
324-
webviewKeys = keys ?? [];
292+
setWebviewKeys(keys);
325293
});
326294

327295
electron.ipcMain.on("set-keyboard-chord-mode", (event) => {

emain/emain-tabview.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { setWasActive } from "./emain-activity";
1212
import { getElectronAppBasePath, isDevVite, unamePlatform } from "./emain-platform";
1313
import {
1414
decreaseZoomLevel,
15+
getWebviewKeys,
1516
handleCtrlShiftFocus,
1617
handleCtrlShiftState,
1718
increaseZoomLevel,
@@ -322,6 +323,20 @@ export async function getOrCreateWebViewForTab(waveWindowId: string, tabId: stri
322323
tabView.webContents.send("webview-new-window", wc.id, details);
323324
return { action: "deny" };
324325
});
326+
wc.on("before-input-event", (e, input) => {
327+
if (input.type != "keyDown") {
328+
return;
329+
}
330+
const waveEvent = adaptFromElectronKeyEvent(input);
331+
handleCtrlShiftState(tabView.webContents, waveEvent);
332+
for (const keyDesc of getWebviewKeys()) {
333+
if (checkKeyPressed(waveEvent, keyDesc)) {
334+
e.preventDefault();
335+
tabView.webContents.send("reinject-key", waveEvent);
336+
return;
337+
}
338+
}
339+
});
325340
});
326341
tabView.webContents.on("before-input-event", (e, input) => {
327342
const waveEvent = adaptFromElectronKeyEvent(input);

emain/emain-util.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ export const WaveAppPathVarName = "WAVETERM_APP_PATH";
88
export const WaveAppResourcesPathVarName = "WAVETERM_RESOURCES_PATH";
99
export const WaveAppElectronExecPath = "WAVETERM_ELECTRONEXECPATH";
1010

11+
let webviewKeys: string[] = [];
12+
13+
export function getWebviewKeys(): string[] {
14+
return webviewKeys;
15+
}
16+
17+
export function setWebviewKeys(keys: string[]) {
18+
webviewKeys = keys ?? [];
19+
}
20+
1121
const MinZoomLevel = 0.4;
1222
const MaxZoomLevel = 2.6;
1323
const ZoomDelta = 0.2;

emain/emain-window.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { delay, ensureBoundsAreVisible, waveKeyToElectronKey } from "./emain-uti
2222
import { ElectronWshClient } from "./emain-wsh";
2323
import { updater } from "./updater";
2424

25-
const DevInitTimeoutMs = 5000;
25+
const DevInitTimeoutMs = 15000;
2626

2727
export type WindowOpts = {
2828
unamePlatform: NodeJS.Platform;
@@ -395,7 +395,7 @@ export class WaveBrowserWindow extends BaseWindow {
395395
const clientId = await getClientId();
396396
await this.awaitWithDevTimeout(tabView.initPromise, "initPromise", tabView.waveTabId);
397397
const winBounds = this.getContentBounds();
398-
tabView.setBounds({ x: 0, y: 0, width: winBounds.width, height: winBounds.height });
398+
tabView.positionTabOffScreen(winBounds);
399399
this.contentView.addChildView(tabView);
400400
const initOpts: WaveInitOpts = {
401401
tabId: tabView.waveTabId,
@@ -461,6 +461,9 @@ export class WaveBrowserWindow extends BaseWindow {
461461
console.log("initializing a new tab", primaryStartupTab ? "(primary startup)" : "");
462462
await this.initializeTab(tabView, primaryStartupTab);
463463
this.finalizePositioning();
464+
} else if (tabView.isWaveReady) {
465+
console.log("reusing wave-ready tab, skipping reinit", tabView.waveTabId);
466+
this.finalizePositioning();
464467
} else {
465468
console.log("reusing an existing tab, calling wave-init", tabView.waveTabId);
466469
tabView.webContents.send("wave-init", tabView.savedInitOpts); // reinit

emain/preload-webview.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ document.addEventListener("contextmenu", (event) => {
2525
// do nothing
2626
});
2727

28+
document.addEventListener("mouseup", (event) => {
29+
// Mouse button 3 = back, button 4 = forward
30+
if (event.button === 3 || event.button === 4) {
31+
event.preventDefault();
32+
ipcRenderer.send("webview-mouse-navigate", event.button === 3 ? "back" : "forward");
33+
}
34+
});
35+
2836
console.log("loaded wave preload-webview.ts");

frontend/app/app-bg.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function AppBackground() {
2424
const tabBg = useAtomValue(env.getTabMetaKeyAtom(tabId, "tab:background"));
2525
const configBg = useAtomValue(env.getConfigBackgroundAtom(tabBg));
2626
const resolvedMeta: Omit<BackgroundConfigType, "display:name"> = tabBg && configBg ? configBg : tabData?.meta;
27-
const style: CSSProperties = computeBgStyleFromMeta(resolvedMeta, 0.5) ?? {};
27+
const style: CSSProperties = computeBgStyleFromMeta(resolvedMeta, 1) ?? {};
2828
const getAvgColor = useCallback(
2929
debounce(30, () => {
3030
if (

frontend/app/tab/tabbar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ const TabBar = memo(({ workspace, noTabs }: TabBarProps) => {
214214
const addBtnWidth = getOuterWidth(addBtnRef.current);
215215
const appMenuButtonWidth = appMenuButtonRef.current?.getBoundingClientRect().width ?? 0;
216216
const workspaceSwitcherWidth = workspaceSwitcherRef.current?.getBoundingClientRect().width ?? 0;
217-
const waveAIButtonWidth = waveAIButtonRef.current != null ? getOuterWidth(waveAIButtonRef.current) : 0;
217+
const waveAIButtonWidth =
218+
!hideAiButton && waveAIButtonRef.current != null ? getOuterWidth(waveAIButtonRef.current) : 0;
218219

219220
const nonTabElementsWidth =
220221
windowDragLeftWidth +
@@ -301,7 +302,7 @@ const TabBar = memo(({ workspace, noTabs }: TabBarProps) => {
301302
// Check if all tabs are loaded
302303
const allLoaded = tabIds.length > 0 && tabIds.every((id) => tabsLoaded[id]);
303304
if (allLoaded) {
304-
setSizeAndPosition(newTabId === null && prevAllLoadedRef.current);
305+
setSizeAndPosition(false);
305306
saveTabsPosition();
306307
if (!prevAllLoadedRef.current) {
307308
prevAllLoadedRef.current = true;

0 commit comments

Comments
 (0)