Commit Graph

11 Commits

Author SHA1 Message Date
陈大猫
5e00e998a8 Add vault drag reorder ordering (#1407) 2026-06-11 16:05:17 +08:00
陈大猫
36e5779d94 perf(terminal): reduce terminal tab-switch and layout jank (#1321)
* perf(terminal): smooth layout drags and faster tab switching

Defer xterm refit during split, sidebar, and host-tree drags while keeping pane containers in sync with live layout measurements. Refactor TerminalLayer into focused sections with TabBridge/memo optimizations and add the terminal host tree sidebar.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(terminal): keep side panels alive and guard session attach races

Prevent terminal boot unmount from leaking backend sessions, keep SFTP/scripts/theme/AI state when switching side tabs, and defer heavy SFTP UI mount so first entry stays responsive.

Co-authored-by: Cursor <cursoragent@cursor.com>

* perf(terminal): reduce tab switch jank

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 03:35:03 +08:00
pplulee
03cd9bc968 feat(snippets): implement snippet variable handling and UI prompts (#1159) 2026-05-31 18:01:43 +08:00
陈大猫
ea5320d94a Fix #954: unify Tooltip styling + replace native selects (#961)
* Fix #954: unify Tooltip styling + replace native selects

Replace native HTML title= tooltips and native <select> dropdowns
with the existing Radix-based Tooltip / Select components so they
share the app's rounded styling, theme tokens and i18n pipeline.
Adds a global TooltipProvider in AppWithProviders so every
descendant Tooltip works without a per-file Provider wrapper.

Scope (driven by the issue #954 examples and "全部都处理" follow-up):

- TerminalLayer toolbar: Add Terminal / Split View / SFTP / Scripts
  / Theme / AI Chat / Move panel / Close panel.
- TopTabs middle bar: quick switcher, more tabs, AI assistant, theme
  toggle, settings; window-control buttons (min/max/close), tray
  close and hotkey reset/disable have their native title dropped per
  the user's explicit opt-out ("可以不用Tooltip,直接全局禁用
  原生title 属性").
- AI panels: AIChatSidePanel session history / new chat / delete,
  ConversationExport, AgentSelector, ChatInput attach / expand /
  permission, ModelSelector, ProviderCard, ai-elements/tool-call.
- SFTP: SftpSidePanel header, SftpBreadcrumb, SftpFileRow,
  SftpPaneToolbar, SftpTabBar, SftpTransferQueue.
- Settings: SettingsPage close, SettingsAppearanceTab theme/accent
  swatches, SettingsFileAssociationsTab edit/remove, SettingsSystemTab
  crash-log paths and global hotkey reset.
- Host vault: HostDetailsPanel (clear / suggestions / show-password /
  key path / browse key), GroupDetailsPanel, KnownHostsManager,
  ConnectionLogsManager, KeychainManager, SyncStatusButton,
  CloudSyncSettings, LogView, QuickSwitcher, ScriptsSidePanel,
  Terminal status bar copy-host + broadcast/focus, ZmodemProgressIndicator.
- Terminal subcomponents: HostKeywordHighlightPopover, TerminalComposeBar,
  TerminalConnectionDialog, TerminalSearchBar.
- Editor: TextEditorPane (subtitle, search, wrap, promote-to-tab).
- TrayPanel session rows and port-forwarding rows.

Native <select> migrated to custom Select component:
- SerialConnectModal (data bits, stop bits, parity, flow control)
- SerialHostDetailsPanel (same four fields)
- HostDetailsPanel backspace behavior
- GroupDetailsPanel backspace behavior
- SettingsTerminalTab local shell picker
- terminal/ThemeSidePanel font weight

Hardcoded English strings extracted to i18n. New keys for both
en and zh-CN: terminal.layer.*, topTabs.*, ai.chat.* (sessionHistory,
attach, collapse, expand, enableAgent), zmodem.*, settings.shortcuts.
resetToDefault. Inline help text on SnippetsManager package-name input
removed because the same hint is already shown in a visible <p> below
the input.

Existing per-file <TooltipProvider> wrappers (SnippetsManager,
ScriptsSidePanel, SelectHostPanel, RuleCard, HostDetailsPanel proxy
section) are left in place — they nest harmlessly under the global
provider and stay self-sufficient for component tests.

Tests:
- tsc clean for changed files (pre-existing repo-wide errors
  unrelated to this PR).
- All 802 tests pass (3 skipped pre-existing).
- HostDetailsPanel.proxyProfile.test and TextEditorPane.test
  updated to wrap with TooltipProvider, matching the runtime
  context now needed by the migrated components.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix #954: wrap Settings + Tray windows with TooltipProvider

Settings and the tray panel mount as separate Electron windows with
their own React root in index.tsx, so they do not inherit the global
TooltipProvider added under AppWithProviders. After the unified
Tooltip migration, any settings tab that used a Tooltip (Appearance,
Application, FileAssociations, System, Shortcuts, Terminal, AI
ProviderCard, AI ModelSelector) — and TrayPanel — threw
"Tooltip must be used within TooltipProvider" and rendered nothing.

Wrap both branches with TooltipProvider at the same level as
ToastProvider in index.tsx.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:14:24 +08:00
陈大猫
f38afd8bfc Align snippet row icons with package row icons in tree (#802)
Snippet rows used a padding-based offset to account for the chevron
column in package rows, but the flex gap between chevron and icon
wasn't being compensated so the FileCode icon sat 4-6px to the left of
the Package icon above it. Mirror the package row's flex layout
literally by rendering an invisible chevron placeholder, so both row
types share the same column structure.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 15:03:06 +08:00
陈大猫
c3dabbfef2 Render snippets sidebar as an expandable tree (#800) (#801)
* Render snippets sidebar as an expandable tree (#800)

The terminal sidebar used breadcrumb navigation, so switching between
packages meant clicking out and back in. Replace that with a single
tree view where each package row has a chevron to expand/collapse
(SFTP-style), so snippets across multiple packages stay visible and
reachable without drilling.

- All discovered packages default to expanded, so the tree matches the
  user's expectation of seeing everything at once.
- Search flattens to a list of matching snippets regardless of nesting,
  each annotated with its package path so the origin is still clear.
- Implicit ancestor packages (e.g. "a/b/c" implies "a" and "a/b") are
  materialized so deeply nested snippets aren't orphaned when a parent
  package isn't explicitly listed.
- Depth-based left padding + chevron rotation mirror the SFTP tree
  view's affordances.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Unify snippet row typography with tree + move command to tooltip

Snippet rows were rendered as two-line blocks (label + inline command
preview), which made them visually taller and heavier than the
single-line package rows in the tree, and long commands overflowed the
container. Collapse them to single-line rows that match the package row
layout exactly (same text size, same padding, aligned icon column) and
surface the full label + command text in a tooltip on hover.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Preserve collapsed packages across snippet refreshes (codex)

The auto-expand effect compared prev.size to normalizedPackages.size to
decide whether to repopulate, but collapsed rows shrink prev.size, so any
later snippet/package change would trip the condition and overwrite the
user's collapse state with a bulk re-expand.

Track the set of packages ever observed in a ref and only auto-expand
paths that are new since the previous render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 14:56:14 +08:00
陈大猫
8ca09b1616 Add right-click Edit/Delete to sidepanel snippets (#780) (#787)
The terminal-side ScriptsSidePanel was the surface the #780 reporter
was actually looking at when they asked for right-click delete/modify
on snippets. PR #783 closed the issue by adding a trash icon in the
Vault edit panel, but the sidepanel snippet rows were still plain
<button>s with no context menu — so the original complaint
("右键可以弹出一个菜单, 可以包含'删除, 修改'等操作") remained unaddressed
at the exact spot the screenshot came from.

Changes:

- ScriptsSidePanel: wrap each snippet row in a ContextMenu with Edit
  and Delete items. Menu actions dispatch window events instead of
  threading new callbacks — matches the existing netcatty:snippets:add
  pattern the + button already uses.
- QuickAddSnippetDialog: accept an optional onUpdateSnippet prop and
  listen for netcatty:snippets:edit. Prefills label/command/package
  from the dispatched snippet, and on save preserves the snippet's
  original tags/targets/shortkey/noAutoRun (the dialog only exposes
  the three quick-edit fields). Title flips to snippets.panel.editTitle
  in edit mode.
- App.tsx: pass onUpdateSnippet wired to updateSnippets(map-replace),
  and register a window listener for netcatty:snippets:delete that
  filters the deleted id out of snippets. Delete needs no UI so it
  doesn't go through a dialog.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:36:52 +08:00
陈大猫
683756324e feat: add "new snippet" button in terminal ScriptsSidePanel (#641) (#656)
* feat: add "new snippet" button in terminal ScriptsSidePanel (#641)

Previously, adding a new snippet required navigating back to the main
Snippets section from the Vault view. This adds a "+" button in the
search header of the terminal-side ScriptsSidePanel that jumps
directly into the snippet edit flow.

Flow:
- ScriptsSidePanel "+" → dispatches window event `netcatty:snippets:add`
- App.tsx listens → switches activeTab to vault, navigates to Snippets
  section, and bumps a monotonic `openSnippetAddTrigger` state
- VaultView forwards the trigger to SnippetsManager
- SnippetsManager watches the trigger and opens its add panel when
  the value changes (uses a ref to ignore unrelated remounts)

Closes #641

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: switch add-snippet flow to one-shot pending flag

Codex review pointed out a real bug with the monotonic trigger approach:
when SnippetsManager mounts for the first time with openAddTrigger already
non-zero (the common "+ clicked from terminal while not on Snippets section"
path), the last-seen-trigger ref is initialized to the current value and
the useEffect immediately returns early, so the add panel never opens.

Switch to a cleaner one-shot pending flag:
- App.tsx holds pendingSnippetAdd: boolean + handlePendingSnippetAddHandled
- VaultView forwards pendingSnippetAdd + onPendingSnippetAddHandled
- SnippetsManager opens the add panel on every transition to pendingAdd=true,
  then clears the flag via onPendingAddHandled, so subsequent renders and
  plain remounts are no-ops

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move useCallback above early return in ScriptsSidePanel

React's rules-of-hooks require all hooks to be called unconditionally.
The new handleAddSnippet useCallback was placed after the
`if (!isVisible) return null;` guard, which tripped eslint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 01:58:34 +08:00
陈大猫
80fbf0da2f feat: add data-section hooks for Custom CSS targeting (#642) (#655)
Custom CSS already exists in Settings → Appearance, but major UI
components use only Tailwind utility classes, making it hard for
users to reliably target regions in their custom styles.

This adds stable `data-section="..."` attributes on the root element
of the most commonly customized UI regions so users can write selectors
like `[data-section="snippets-panel"] { font-size: 14px !important; }`
without depending on implementation details.

Instrumented regions:
- snippets-panel (ScriptsSidePanel)
- host-details-panel (HostDetailsPanel via AsidePanel dataSection prop)
- group-details-panel (GroupDetailsPanel)
- serial-host-details-panel (SerialHostDetailsPanel)
- ai-chat-panel (AIChatSidePanel)
- vault-view / vault-sidebar / vault-main / vault-hosts-header / vault-host-list (VaultView)
- terminal-workspace / terminal-workspace-sidebar (TerminalLayer)
- top-tabs (TopTabs — also keeps existing data-top-tabs-root)

Also updated the Custom CSS description and placeholder in both
English and Chinese to list available hooks and show a working
example (snippet panel font-size override).

Closes #642

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 01:38:50 +08:00
陈大猫
338ba94d42 feat: add paste-only option for snippets (no auto-execute) (#375)
* feat: add "paste only" option for snippets (no auto-execute)

Add a noAutoRun flag to snippets that pastes the command into the
terminal without appending a carriage return, so users can review
and edit before manually pressing Enter.

Applies to all snippet execution paths: snippet runner (new session),
keyboard shortcut, and startup command.

Closes #371

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use clearer wording "仅粘贴" instead of "仅上屏"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip onCommandExecuted for paste-only shortcut snippets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: persist noAutoRun on save and apply to Scripts panel clicks

- Include noAutoRun in handleSubmit serialization (was being lost)
- Pass noAutoRun through ScriptsSidePanel click handler to TerminalLayer
  so paste-only snippets work from the Scripts panel too

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:09:17 +08:00
陈大猫
4877c934fa feat: move Scripts and Theme to side panel sub-tabs (#333)
* feat: move Scripts and Theme from toolbar popups to side panel sub-tabs

Migrate Scripts (snippet library) and Theme customization from toolbar
popover/modal dialogs into the left side panel alongside SFTP. The panel
header now shows three tab buttons (SFTP / Scripts / Theme) so users can
switch between sub-panels without losing SFTP connections.

- Add ScriptsSidePanel with package hierarchy, breadcrumb nav and search
- Add ThemeSidePanel adapted from ThemeCustomizeModal (no preview pane)
- Generalize TerminalLayer state from sftpOpenTabs to sidePanelOpenTabs
- Simplify TerminalToolbar by removing inline popover and modal rendering
- Clicking the already-active tab button is a no-op; only X closes panel
- Theme/font changes apply in real-time to the actual terminal behind

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings for side panel migration

- Clean up sftpInitialLocationForTab on panel close
- Remove unused handleCloseSidePanel from deps array
- Re-focus terminal after snippet execution from side panel
- Use props directly in ThemeSidePanel instead of mirrored local state
- Use ?? instead of || for falsy-safe theme/font/size defaults
- Extract isFocusedHostLocal into memoized value

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:21:34 +08:00