Skip to content

Feat/file tree row menu - #648

Open
mrytsr wants to merge 4 commits into
xintaofei:mainfrom
mrytsr:feat/file-tree-row-menu
Open

Feat/file tree row menu#648
mrytsr wants to merge 4 commits into
xintaofei:mainfrom
mrytsr:feat/file-tree-row-menu

Conversation

@mrytsr

@mrytsr mrytsr commented Sep 4, 2026

Copy link
Copy Markdown

背景

文件树在桌面端可以右键打开菜单,触屏端之前依赖长按。长按接下来要留给拖拽用,所以触屏端需要另一条入口。

改动

每个文件/文件夹/工作区根目录行的右侧加一个横向三点的 RowMoreButton。点击时阻止冒泡,向最近的 [data-tree-row-path] 祖先派发一个合成的 contextmenu MouseEvent。Radix 已有的 ContextMenuTriggerasChild 包裹整行)收到这个事件就打开和右键、长按完全同一个菜单——零菜单项重复。

  • FileTreeFile / FileTreeFolder 新增 actions?: ReactNode,渲染在已有的 FileTreeActions 包裹里(自动 stopPropagation)。
  • RowMoreButton(新组件 + 3 个单测)处理按钮的渲染和派发逻辑。
  • Folder.fileTreeTab.moreActions 文案 10 个语言全量新增。
  • useLongPressToOpenMenu 保留(测试一起恢复)备用,文件树不再依赖它。

验证

  • eslint . → exit 0
  • vitest run → 5786 个测试通过(406 个文件;比之前 +13:恢复的 10 个 hook 测试 + 新增的 3 个 RowMoreButton 测试)
  • next build → 成功

Your Name added 4 commits September 3, 2026 21:43
Add an on-screen virtual key bar for the terminal panel so mobile users
can send escape / tab / control / arrow / page keys without a hardware
keyboard. The bar mirrors the pios implementation: two rows of 14 buttons
(ESC / / — HOME ↑ END PGUP | TAB CTRL ALT ← ↓ → PGDN) with a CTRL/ALT
latch that wraps the next soft-keyboard input as a control byte or ESC
prefix respectively.

Pure encoding lives in lib/terminal/keybar.ts — 12 keys + termKeySeq +
applyTermMods + kbdLiftPx — covered by 18 unit tests. The new
<TermKeybar /> component is purely presentational and uses shadcn Button
+ Tailwind utilities, so there is no raw CSS.

Wiring:
  - terminal-panel.tsx adds useMediaQuery('(max-width: 768px)') and a
    localStorage-backed collapse flag (codeg:term-keybar) shared across
    all tabs, then forwards keybarVisible down.
  - terminal-tab-bar.tsx gains a ⌨ toggle button (mobile-only, with
    tooltip + active highlight) that flips the flag.
  - terminal-view.tsx exposes writeQueueRef so the key bar's bytes feed
    the existing ordered single-flight write pump (without this the key
    bar would bypass the queue and scramble with onData on slow transport).
    onData is wrapped with applyTermMods to honor the latch. The
    visualViewport observer drives a kbdLift max-height on the outer flex
    column so the bar lifts above the soft keyboard and xterm auto-refits
    via the existing ResizeObserver.
  - workspace/layout.tsx bumps the mobile Drawer from 70vh to 95vh to
    give the new bar enough room when folded in.

i18n: 16 new keys under Folder.terminal.keybar across all 10 locales
(symbols are language-neutral; show/hide/label are translated for
zh-CN/zh-TW/ja/ko/es/de/fr/pt/ar). English fallbacks keep the type
contract aligned.

Desktop path is untouched: useMediaQuery stays false, the toggle and
the bar never render, and the writeQueueRef sits at null.
The three ContextMenuTrigger wrappers around FileTreeFile,
FileTreeFolder, and the RootDropFolder root-drop menu rendered as bare
<span> elements. HTML parser rules disallow <span> from containing <div>
children, so the row div ended up as the span's sibling and Radix's
pointerdown / contextmenu handlers — and its WebkitTouchCallout: none
style — never reached the actual row.

Effect on touch:
* iOS Safari showed its native long-press callout (Copy / Share / Save
  Image), eating the gesture before Radix's 700ms long-press timer could
  open the menu.
* Android Chrome and desktop touchscreens likewise had no path to open
  the per-row menu.

Desktop right-click worked because onContextMenu propagates by capture
and bubble independently of the parent-child DOM relationship.

Fix: pass asChild to each of the three ContextMenuTrigger instances so
Radix's Slot mechanism merges the trigger props onto the row's own div.
This matches every other ContextMenuTrigger usage in the codebase
(tabs/tab-item, automations-page, settings/skills-settings,
settings/mcp-settings, conversations/sidebar-conversation-list, etc.).
Bring the mobile virtual key bar for the terminal into main.
Touch devices don't get a free way to open the file-tree context menu now
that the long-press gesture is reserved for drag. Add a small horizontal
three-dots button (MoreHorizontal) on the right of every file, folder,
and workspace-root row. Clicking it dispatches a synthetic contextmenu
MouseEvent on the row so Radix's existing ContextMenuTrigger opens the
same menu the right-click and long-press paths open — no menu items
duplicated.

- FileTreeFile / FileTreeFolder gain an optional actions?: ReactNode prop
  rendered inside the existing FileTreeActions wrapper (which already
  stops click bubbling).
- RowMoreButton (new component) walks up to the nearest
  [data-tree-row-path] ancestor and dispatches the contextmenu event.
- Folder.fileTreeTab.moreActions added to all 10 locales.
- useLongPressToOpenMenu is kept (and its tests restored) for reuse
  elsewhere; the file tree now relies on the more button + native
  right-click instead of the long-press hook.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant