diff --git a/docs/docs/keybindings.mdx b/docs/docs/keybindings.mdx index fa8dcae1ba..36ca33a9ce 100644 --- a/docs/docs/keybindings.mdx +++ b/docs/docs/keybindings.mdx @@ -6,6 +6,7 @@ title: "Key Bindings" import { Kbd, KbdChord } from "@site/src/components/kbd"; import { PlatformProvider, PlatformSelectorButton } from "@site/src/components/platformcontext"; +import { VersionBadge } from "@site/src/components/versionbadge"; @@ -44,6 +45,7 @@ Chords are shown with a + between the keys. You have 2 seconds to hit the 2nd ch | | Switch to block number | | / | Move left, right, up, down between blocks | | | Replace the current block with a launcher block | +| | Rename the current tab | | | Switch to tab number | | / | Switch tab left | | / | Switch tab right | diff --git a/frontend/app/store/keymodel.ts b/frontend/app/store/keymodel.ts index afa5209116..3df35f9ba3 100644 --- a/frontend/app/store/keymodel.ts +++ b/frontend/app/store/keymodel.ts @@ -634,6 +634,14 @@ function registerGlobalKeys() { ); return true; }); + globalKeyMap.set("F2", () => { + const tabModel = getActiveTabModel(); + if (tabModel?.startRenameCallback != null) { + tabModel.startRenameCallback(); + return true; + } + return false; + }); globalKeyMap.set("Cmd:g", () => { const bcm = getBlockComponentModel(getFocusedBlockInStaticTab()); if (bcm.openSwitchConnection != null) { diff --git a/frontend/app/store/tab-model.ts b/frontend/app/store/tab-model.ts index a867440820..75eeb479a7 100644 --- a/frontend/app/store/tab-model.ts +++ b/frontend/app/store/tab-model.ts @@ -21,6 +21,7 @@ export class TabModel { tabNumBlocksAtom: Atom; isTermMultiInput = atom(false) as PrimitiveAtom; metaCache: Map> = new Map(); + startRenameCallback: (() => void) | null = null; constructor(tabId: string, waveEnv?: TabModelEnv) { this.tabId = tabId; diff --git a/frontend/app/tab/tab.tsx b/frontend/app/tab/tab.tsx index 7b2aa6856e..4972a13daa 100644 --- a/frontend/app/tab/tab.tsx +++ b/frontend/app/tab/tab.tsx @@ -3,6 +3,7 @@ import { getTabBadgeAtom } from "@/app/store/badge"; import { refocusNode } from "@/app/store/global"; +import { getTabModelByTabId } from "@/app/store/tab-model"; import { TabRpcClient } from "@/app/store/wshrpcutil"; import { WaveEnv, WaveEnvSubset, useWaveEnv } from "@/app/waveenv/waveenv"; import { Button } from "@/element/button"; @@ -251,6 +252,7 @@ const TabInner = forwardRef((props, ref) => { const loadedRef = useRef(false); const renameRef = useRef<(() => void) | null>(null); + const tabModel = getTabModelByTabId(id, env); useEffect(() => { if (!loadedRef.current) { @@ -259,6 +261,16 @@ const TabInner = forwardRef((props, ref) => { } }, [onLoaded]); + useEffect(() => { + const cb = () => renameRef.current?.(); + tabModel.startRenameCallback = cb; + return () => { + if (tabModel.startRenameCallback === cb) { + tabModel.startRenameCallback = null; + } + }; + }, [tabModel]); + const handleTabClick = () => { onSelect(); }; diff --git a/frontend/app/tab/vtab.tsx b/frontend/app/tab/vtab.tsx index 7edd7b0f45..4c70d5ec37 100644 --- a/frontend/app/tab/vtab.tsx +++ b/frontend/app/tab/vtab.tsx @@ -1,6 +1,7 @@ // Copyright 2026, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 +import { refocusNode } from "@/app/store/global"; import { validateCssColor } from "@/util/color-validator"; import { cn } from "@/util/util"; import { useCallback, useEffect, useRef, useState } from "react"; @@ -122,6 +123,7 @@ export function VTab({ if (newText !== originalName) { onRename?.(newText); } + setTimeout(() => refocusNode(null), 10); }; const handleKeyDown: React.KeyboardEventHandler = (event) => { diff --git a/frontend/app/tab/vtabbar.tsx b/frontend/app/tab/vtabbar.tsx index f8cbc751fb..e40bcfb374 100644 --- a/frontend/app/tab/vtabbar.tsx +++ b/frontend/app/tab/vtabbar.tsx @@ -3,6 +3,7 @@ import { Tooltip } from "@/app/element/tooltip"; import { getTabBadgeAtom } from "@/app/store/badge"; +import { getTabModelByTabId } from "@/app/store/tab-model"; import { makeORef } from "@/app/store/wos"; import { TabRpcClient } from "@/app/store/wshrpcutil"; import { useWaveEnv } from "@/app/waveenv/waveenv"; @@ -121,6 +122,17 @@ function VTabWrapper({ const [tabData] = env.wos.useWaveObjectValue(makeORef("tab", tabId)); const badges = useAtomValue(getTabBadgeAtom(tabId, env)); const renameRef = useRef<(() => void) | null>(null); + const tabModel = getTabModelByTabId(tabId, env); + + useEffect(() => { + const cb = () => renameRef.current?.(); + tabModel.startRenameCallback = cb; + return () => { + if (tabModel.startRenameCallback === cb) { + tabModel.startRenameCallback = null; + } + }; + }, [tabModel]); const rawFlagColor = tabData?.meta?.["tab:flagcolor"]; let flagColor: string | null = null;