Split vault/session/settings into publishers and store-backed Hosts so hot
fields no longer rebuild the whole shell, and harden Terminal boot / startup
effects for StrictMode double-mounts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor: migrate architecture-debt hooks into application/state
Move SFTP Wave A stores/hooks and related vault/AI/terminal/systemManager
hooks out of components into application/state, update importers, and keep
thin deprecated re-exports at the old paths.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* refactor(sftp): align local bookmarks store API with global bookmarks
Export subscribe/get/set/rehydrate helpers from localSftpBookmarks to match
the globalSftpBookmarks external-store pattern.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(electron): reduce baseline memory via spellcheck/GPU/prewarm guards
Disable spellcheck on BrowserWindows and the default session, drop spare
renderers, gate aggressive GPU switches behind NETCATTY_COMPAT_GPU, and make
settings prewarm opt-in via NETCATTY_PREWARM_SETTINGS.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* Isolate notes and customAccent from TerminalLayer hot props
Route vault notes through notesStore (shellHistoryStore pattern) and
app accent through appearanceChromeStore so note edits and accent-drag
ticks no longer rebuild TerminalLayer. Notes/AI slots and Terminal leaves
subscribe locally; VaultView and open-note callbacks stay unchanged.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(ui): cut unnecessary AppView/VaultView/settings re-renders
Stabilize TopTabs close handler, memo AppHostTreeLayer, exclude accent from
settingsVersion, ignore connectionLogs off the logs section, share lastConnected
host refs, and keep rename dialog drafts local so chrome domain stays stable.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* chore: package size, dead code, and consolidation cleanup
Remove unused direct deps, exclude Vite-bundled renderer packages from
electron-builder, delete high-confidence dead code/no-op transfer warm
wiring, consolidate cloud provider IDs and KnownHost/SFTP helpers, and
land UI/AGENTS quick wins (rAF text batching, notes draft debounce,
streaming plain text, tempDirBridge, diagnostics adapter, External MCP
setters).
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: stabilize rAF text batching and diagnostics tests
Polyfill requestAnimationFrame for Node, flush pending text before
steer buffering, bypass rAF while steer is in flight, and stub
global localStorage for AI panel diagnostics adapter tests.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: correct import paths after application/state hook migrations
Migrated vault/AI/system hooks still used components/-relative imports;
point them at repo-root domain/components/types/lib instead.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: address Bugbot/Codex regressions from perf cleanup
- Keep blank Enter from closing RenameDraftDialog; only reset rename state after a non-empty submit
- Flush note drafts before vault mutations so pending title/body edits are not overwritten
- Re-apply appearanceChromeStore accent on appearanceTheme in Terminal, and keep layer themes unaccented
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): flush draft before markdown export paths
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: keep editor-close hotkey ref fresh during render
Assign handleRequestCloseEditorTabRef in render instead of useEffect so
Cmd/Ctrl+W never hits the App stub before the effect runs.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: keep accent in chrome themes without TerminalLayer thrash
Restore accent in globalAppearance for chrome/injection consumers.
Keep resolveFocusedAppearance identity stable across accent drag via
accent ref, while Terminal leaf still re-applies appearanceChromeStore.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: keep compose chrome accent live and merge note renames
Subscribe theme-panel focusedAppearance to appearanceChromeStore so
compose-bar chrome updates during color-picker drag even when
resolveFocusedAppearance identity stays stable. Tree renames now merge
title into the post-flush note instead of replacing from a stale row.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: clear terminal accent when switching back to theme mode
Re-resolve appearanceTheme by catalog id before applyCustomAccent so a
stale baked custom cursor/selection cannot stick after accentMode returns
to theme while TerminalLayer memo ignores accent props.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: register unsaved prompt singleton during render
AppView close handlers call promptUnsavedChanges outside the old
render-prop. Assign the singleton during render so Cmd/Ctrl+W and tab
close never hit the pre-effect null window that auto-cancels.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: keep vault import hooks UI-free and flush notes on teardown
Move ImportOptions into application/state and inject a notifier so
useVaultImportHandlers no longer imports components. Notes drafts also
flush on pagehide/beforeunload when Electron tears down without React
unmount.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: move AI terminal session context builders into domain
buildAITerminalSessionInfo and AIPanelContext now live under domain/ so
application hooks no longer import TerminalLayerSupport and create a
UI → application → UI cycle.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: drop duplicate AIPanelContext type from TerminalLayerSupport
The type is re-exported from domain; the local definition conflicted.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: keep migrated state hooks free of UI-layer imports
Move aiChatStreamingSupport into infrastructure/ai and poll list helpers
into domain/systemManager so useAIChatStreaming / useSystemManager no
longer import from components. Leave thin re-exports at the old paths.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: move remaining migrated-hook deps out of components
Host-key verification types/helpers now live in domain/, and SFTP column
layout types live in application/state/sftp/columnLayout. Application
hooks no longer import those definitions from the UI layer.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: clear lint blockers and flush notes when panels hide
Move AI panel diagnostics persistence into application/state so
components stop importing localStorageAdapter. Flush note drafts when
retained NotesManager mounts become inactive, and silence related lint
warnings in theme/host-key helpers.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): publish notesStore sync on vault note updates
updateNotes/updateNoteGroups now publishNotesSnapshot immediately so
AI/notes consumers do not briefly read a stale catalog after a hide
flush. Flush inactive drafts in useLayoutEffect before paint.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
Chinese glyphs (PingFang etc.) paint outside a 1.0 em box, so leading-none
plus truncate was slicing vault tree, tabs, and dialog titles. Use safer
line-heights and slightly taller note title inputs.
* perf(vault): virtualize host pickers for large inventories
Virtualize Select Host, Quick Switcher, SFTP host picker, and workspace
dialogs so thousands of hosts only mount a viewport window. Shared scroll
and keyboard clamp helpers keep selection and filter-while-scrolled paths
stable under virtualization.
* fix: address Codex review on PR #2553
* fix(vault): plugin row heights and Select Host keyboard types
Give Quick Switcher plugin command/view rows a taller virtual slot so
two-line chrome does not overflow into the next absolute row. Narrow
Select Host navigable rows to group|host so Enter keyboard activation
typechecks cleanly.
* fix(vault): finish host-picker review feedback
Clip and truncate fixed-height Quick Switcher / workspace rows so long
labels never overlap the next virtual slot. Wire Select Host listbox
aria-activedescendant to stable option ids for keyboard AT feedback.
* fix: address Codex review on PR #2553
* fix: address Codex review on PR #2553
* fix: address Codex review on PR #2553
* fix: address Codex review on PR #2553
* fix(vault): clear remaining host-picker codex findings
Declare multi-select listboxes, drop empty SFTP Hosts headers when only
connected rows remain, size the SFTP list to content under a 360px cap,
and give Quick Switcher / workspace pickers a definite max scroll height
so large inventories remain reachable.
---------
Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com>
* feat: fold top-bar utilities into a control panel
Collapse External MCP, window opacity, theme, and cloud sync into one top-bar control panel so the right rail only keeps AI, controls, and settings.
* fix: keep the cloud sync panel and append quick controls
Restore the original detailed cloud sync popover as the host surface, and place External MCP, window opacity, and theme controls in a footer section beneath it.
* fix: split quick controls from the cloud sync panel
Restore the original cloud sync popover and move External MCP, window opacity, and theme into a dedicated quick-controls entry so the two surfaces stay separate.
* fix: simplify quick controls panel chrome
Drop the explanatory subtitle and card-heavy rows so the panel reads as a compact control list.
* fix: separate appearance controls from External MCP
Group window opacity and theme under one appearance section, with a divider from the External MCP switch.
* fix: compact window opacity controls in the quick panel
Replace the tall slider-plus-presets block with a single-row preset selector to free vertical space.
* fix: make window opacity controls expandable
Keep the opacity row compact by default and reveal the slider plus presets only when the row is expanded.
* fix: put appearance controls above External MCP
Reorder the quick controls panel so window opacity and theme sit first, with External MCP below the divider.
* fix: harden quick controls opacity matching and drop dead copy
Compare opacity presets by percent to avoid float mismatch, and remove the unused quick-controls description strings.
* fix: clean quick controls accessibility and dead top-bar opacity code
Move Switch accessible names onto the focusable control, restore popover autofocus, delete the unused window opacity button, and drop obsolete top-bar theme copy.
* fix: tighten quick controls peer-window and switch labeling
Assert showExternalMcpToggle is forwarded into the quick panel, and name the External MCP switch from its visible label only.
* fix: add icon to quick controls panel title
Prefix the panel header with the same sliders icon used by the top-bar entry.
* fix: remove icon from quick controls panel title
Keep the header as plain text after the title icon felt unnecessary.
* feat(settings): support local CJK fonts
* refactor: move CJK font rules to domain
* fix(settings): improve CJK font preview safety
* fix(settings): refresh local font previews
* fix(settings): improve font picker keyboard behavior
* fix(settings): handle unavailable font detection
* fix(settings): cover font picker edge cases
* fix(settings): quote custom font families safely
When a menu cannot fit above or below the trigger, stop flipping it
off-screen. Prefer the side with more space and cap max-height so
overflow scrolls instead of clipping trailing actions.
Fix DropdownContent side=top so the menu sits fully above the trigger
instead of covering it. Match QuickConnect continue/save menu styling
to TerminalAuthDialog (open below, same z-index).
Fixes#2194
* feat(ui): customizable dense toolbars with show/collapse/hide
Add a shared toolbar layout model so terminal, SFTP, and side-panel
action strips can right-click customize placement, overflow collapsed
items into the more menu, and persist order plus placement.
* fix(ui): harden toolbar layout placement after review
Address review findings: availability-aware reachability, skip
unavailable reorder neighbors, Popover overflow for nested SFTP menus,
path chrome outside customize context menu, and side-panel hide close.
* fix(ui): only close side panel when hide placement sticks
Return the next layout from setPlacement so hide-of-last-reachable
does not tear down the panel, keep an active hidden tab chip visible,
and rebuild terminal shown order with highlight collapse coercion.
* fix(ui): narrow SFTP overflow spill and close ⋮ after leaf actions
Respect user show/collapse/hide, then temporarily move non-pinned shown
actions into ⋮ when the SFTP toolbar is narrow. Close overflow after
leaf clicks while keeping nested bookmark/encoding panels usable.
* feat(scripts): add terminal script automation, recording, and side panel
Introduce nct script runtime in the main process with terminal-side recording,
execution overlay, Monaco editor, connect-script queues, and scripts side panel
integration. Fix recording to wire scriptRecorderRef into xterm, use waitForPrompt
for root/user shells, and save recorded scripts through the global editor dialog.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): address Bugbot review findings for recording and side panel
Refresh script run state in side panel memo, guard toolbar recording against
multi-session conflicts, reset connect scripts on reconnect, resolve focused
session via getActiveTerminalSessionId, and avoid wiping host edit form when
snippets list updates.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): prevent pending script re-run and improve workspace run feedback
Track one-shot pendingScript execution separately from onConnect queue so
reconnects do not replay Run-now scripts. Fall back to focused session when
running on all tabs outside a workspace, and toast when every tab is still
connecting.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): consume connect-script window once and filter serial hosts
Mark the onConnect script evaluation as consumed even when the queue is
empty so later snippet/host edits do not rerun automation mid-session.
Restore serial-host filtering in SelectHostPanel confirm handler.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): grace period before giving up on connect-script queue
Only mark onConnect automation as consumed after scripts run or a short
post-connect grace window expires, so late-loading snippets still trigger
while mid-session Vault edits do not.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): gate connect-script consumption on vault hydration
Replace the grace timer with vault-init and unresolved host script binding
checks so late-loading snippets still run onConnect automation while
mid-session edits stay suppressed. Clear pending script run state on
disconnect so reconnect retries workspace pending scripts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): honor run targets and retry failed connect automation
Route QuickScriptEditorDialog Run through onRunSnippet for multi-host
workspaces. Defer connect/pending script completion markers until each
script succeeds and retry the queue after failures.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): stabilize pending runs and global connect queue sync
Keep pending script run state across reconnects, run onConnect scripts
before one-shot workspace pending scripts, and strip per-host queue entries
when an onConnect script targets all hosts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): defer host queue snippet sync and guard connect runs
Persist snippet target links only when the host panel is saved, block
duplicate connect-script scheduling while a batch is running, and require
runnable hosts before showing Run in the snippets editor footer.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): retry connect failures and surface output-trigger errors
Use a retry tick in connect-script effect deps, toast automation failures
from output triggers, and require runnable hosts before QuickScript Run.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): track id-less pending runs and re-arm output triggers
Remember anonymous pendingScript objects after first run and allow
onOutput triggers to fire again when new terminal output arrives.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): unblock paused connect runs and pass session meta
Resolve waitForScriptRun on pause, run workspace scripts per session
with hostname context, defer output-trigger arming until runs start, and
add noRunnableHosts i18n for QuickScript Run.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): serialize output triggers and defer empty connect finalize
Fire at most one onOutput trigger per scan while a run is active, and
only finalize an empty connect queue after vault snippets are loaded.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): guard output launch race and script run waits
Block overlapping onOutput launches, timeout stuck waitForScriptRun
promises, and disable vault Run when no connectable targets exist.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): match output triggers on new chunks only
Scan onOutput patterns against recent terminal output instead of buffer
length dedupe, and restore overflow-visible on SelectHostPanel new-host flow.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): await sequential workspace runs and output launch
Wait for each tab script to finish in sequential workspace mode, and hold
output-trigger launch guard until the session run becomes active.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): wait through pause and hold output trigger until done
Keep connect and sequential workspace queues blocked until scripts
complete or fail, and retain output-trigger guard until the launched run
finishes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): harden output trigger wait helpers
Add explicit timeouts to output-trigger wait helpers and stop rescanning
the buffer after script completion to avoid sticky-match rerun loops.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): allow empty connect queue and match output in new chunks
Treat an empty connectScriptIds array as an explicit cleared queue instead
of re-migrating from snippet targets, and only fire onOutput triggers when
the latest terminal output contains the pattern.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): require fresh output overlap for onOutput triggers
Only fire output-matched scripts when the regex match extends into the
latest terminal chunk, preventing reruns from stale buffer text.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): promote manual scripts added to host connect queue
Diff connect queue changes from raw connectScriptIds so saving a host
updates manual scripts to onConnect when they are newly queued.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): dedupe output triggers by match position
Track the buffer end offset of each output trigger match and ignore
stale re-scans, with a longer wait for run registration before retry.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): record output trigger matches only after run starts
Propagate automation launch failures to output triggers and commit match
dedupe only once the session run is registered as active.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): address Codex review on wait cursor and sensitive codegen
Track scan offset so waitFor/waitForAny only match output after the previous wait, and emit sendLine after sensitive prompt in recorded scripts.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(scripts): harden onOutput triggers against PTY echo and local input
Filter server output before pattern matching so onOutput fires only after
Enter on real stdout, not on keystroke/paste echo, OSC/SGR sequences, or
alternate-screen apps. Add dev diagnostics and scope triggers to the active
session when no explicit host targets are configured.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(scripts): remove onOutput trigger debug logging
Drop temporary diagnostics module and console traces now that PTY echo
filtering is verified; keep the production trigger pipeline unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Let users pick icon styles in Appearance settings with live Dock/window updates, dev public asset resolution, and cross-window sync.
Co-authored-by: Cursor <cursoragent@cursor.com>
Route global hotkey/tray show paths through restoreWindowInputFocus, recover renderer focus on visibility/show, dismiss transient overlays before native hide, and cancel deferred focus callbacks on hide.
Closes#1722
* feat(terminal): add remote command history side panel
Read remote shell history over SSH/ET/Mosh exec channels, browse it in a virtualized side panel with search, paste, and save-as-snippet actions. Closes#1381.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): expand command detail inline below selected row
Move the detail strip from a fixed slot above the list into the row
immediately below the clicked entry so expansion reads top-to-bottom.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): filter Netcatty AI PTY commands from remote history
Drop shell history lines containing the __NCMCP_ marker so AI exec noise
does not clutter the command history panel.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): tighten detail strip and add run action
Size the expanded row to its content, add a run-in-terminal button, and
use clearer snippet icon/tooltip for save-as-snippet.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(history): address review findings before merge
Key cache by host+session, retry Mosh pending reads, and clamp virtual
list scroll position when filtered items shrink.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* 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>
* Replace app logo across window icon, tray, splash, and in-app brand
- public/logo.svg: new netcatty mark
- public/icon.png: regenerated 1024x1024 from new SVG (source for
electron-builder — .icns/.ico rebuilt automatically at pack time)
- public/dmg-fix-icon.png: regenerated 1024x1024
- public/tray-icon{,@2x}.png: regenerated color 16/32px for Linux/Windows
- public/tray-iconTemplate{,@2x}.png: regenerated monochrome silhouette
for macOS menu bar (background stripped, foreground flattened to
black on transparent so template-image rendering produces a clean
mask)
- components/AppLogo.tsx: render the new logo as a static <img>. The
old hand-coded inline SVG bound fills to the accent CSS variable;
the new mark has a fixed palette, so callers keep their sizing /
rounding classes via className while the asset itself is a single
file served from /public.
- index.html: splash screen now uses the same /logo.svg via <img>,
with border-radius for the rounded-square frame.
* Polish logo: theme the in-app mark, gloss the OS icon, shrink cat
- components/AppLogo.tsx: back to an inline SVG. Background rect fills
with hsl(var(--primary)) so the in-app brand follows the theme
accent (was fixed navy when imported as <img>). Cat scaled to 68%
of the frame and centred so it doesn't crowd the edges at small
sidebar sizes.
- public/logo.svg + regenerated PNGs: polished OS icon variant with a
large rounded-square clip (rx 224 on 1024), top-left spotlight
radial gradient, subtle top sheen + bottom darkening, and an inner
edge vignette for a slight chamfer. The cat is shrunk to the same
68% as the in-app logo for visual consistency.
- Monochrome tray template (macOS menu bar) is rebuilt from the
shrunk-cat path set with all fills flattened to black; keeps a
clean silhouette instead of a filled rounded square.
* Smooth paws, richer gloss on app icon
- Drop the dark toe/claw detail paths from the source illustration
(indices 22-25, 30, 35, 37, 39 — the ones tracing vertical claw
dividers inside the paws). At small sizes those read as teeth/
claws; paws now render as clean rounded blobs.
- public/logo.svg (OS icon source): richer depth pass —
* two-tone navy vertical gradient (lighter top, deeper bottom)
* brighter upper-left spotlight for glassy highlight
* top sheen + bottom darkening for sheen-across-curve effect
* soft elliptical ground shadow beneath the cat to anchor it
* 2% inner edge stroke to crisp the rounded-square chamfer
- components/AppLogo.tsx: regenerated with the same cleaned cat set,
still themed via hsl(var(--primary)). The in-app mark stays flat
(no gloss) because the effect adds nothing at 20-40px sidebar
sizes and would fight theme accents.
- All raster variants (icon.png, dmg-fix-icon.png, tray color + tray
macOS template) rebuilt from the cleaned sources.
* Respect Apple icon safe area; drop gloss, add thin border
macOS icon was rendering to the full 1024x1024 canvas, so it looked
noticeably larger than neighbour apps (VS Code, Ghostty, Zed) in the
Dock. Apple's Big Sur+ convention puts the artwork body inside an
~824x824 safe area centred in a 1024 canvas, which is how those apps
are sized.
- public/logo.svg: artwork body is now 824x824 centred with ~100px
transparent padding. Corner radius 185 (close enough to the macOS
squircle at Dock scale). Cat rescaled so it keeps the same 68%
proportion within the smaller body.
- Gloss layers (spotlight / sheen / ground shadow / vignette) removed
per request — went for a Ghostty-style clean look instead.
- Thin white inner border (stroke 3px, 22% opacity) outlines the
rounded square for definition.
- Tray PNGs for Linux/Windows keep the full-bleed variant (tray slots
expect the icon to fill the space, unlike the Dock safe area).
- components/AppLogo.tsx unchanged conceptually — it still fills its
own bounding box via hsl(var(--primary)); the Apple safe-area rule
is Dock-specific, not relevant to in-app rendering.
* AppLogo: tighten corner radius to match previous (rx 18.75%)
Previous AppLogo used rx=12 on a 64 viewBox (18.75%). The inline
replacement had rx=224 on a 1024 viewBox (21.9%), which combined
with the caller's rounded-xl class read noticeably rounder in the
sidebar. Drop to rx=192 on 1024 viewBox so the in-app mark matches
the old proportions.
* Beef up icon border so it survives Dock downscaling
3 px at 22% opacity disappeared when rasterised down to ~128 px Dock /
Launchpad size. Bumped stroke-width to 8 px and opacity to 40% so the
inner highlight reads as ~1 px at Dock scale. Stroke is inset by
stroke-width/2 so it sits fully inside the rounded-square body (no
anti-alias bleed outside the safe area). Same treatment applied to the
full-bleed tray variant.
* Enlarge cat inside icon tile (68% -> 85% of body)
Dock render had too much navy margin around the mark. Bump the cat's
scale so it fills 85% of the Apple safe-area body while keeping a
visible bezel to the rounded corners and the inner border. Tray color
variant and macOS template (scale 0.9, no border) follow the same
scale-up.
* Add ripple effect on sidebar nav and tidy logo in vault header
- Add RippleButton wrapper + ripple keyframe; use it for the six vault
sidebar nav entries (Hosts, Keychain, Port Forwarding, Snippets,
Known Hosts, Logs) so clicks get a subtle material-style ripple.
- Shrink vault sidebar AppLogo to h-8 w-8 and drop the outer rounded-xl
so the visible corner comes from the SVG's own rx instead of the
container clip.
- Relax AppLogo tile rx/ry to 144 for a more moderate corner radius.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* AppLogo: bump tile corner radius back up to rx 18.75%
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Unify manager toolbars, tighten tabs and vault sidebar title
- Manager toolbars (Keychain, KnownHosts, PortForwarding, Snippets)
normalised to h-14 / h-10 controls with bg-secondary/80 backdrop-blur
and the shared bg-foreground/5 secondary button treatment, so Hosts /
Keychain / Known Hosts / Port Forwarding / Snippets headers size and
tint identically.
- Keychain filter tabs: drop primary tint and cert-count pill; reuse
the same foreground/5 vs foreground/10 active states as other
managers. Search input grown to h-10 to match.
- Known Hosts: removed the leftover text-xs on Scan System / Import
File so they inherit Button's text-sm like every other action.
- TopTabs: drop the 2px active-accent top line and add rounded-t-md +
overflow-hidden so active tabs read as a clean soft tab shape rather
than a banner.
- VaultView sidebar: wordmark grown to text-xl font-black italic with
tightened tracking; logo gap trimmed from 3 to 2.5; outer bg dropped
from secondary/80 to flat secondary to sit flush against the
toolbars.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
* feat(i18n): add translations for group config panel
* feat(models): add GroupConfig data model, resolution logic, and encryption
Add the GroupConfig interface for group-level default settings that hosts
inherit. Includes ancestor-chain resolution (A/B/C merges from A, A/B,
A/B/C), host-level application logic, storage key, and secure field
encryption/decryption for sensitive GroupConfig fields.
Part of #220.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(state): add groupConfigs state management with encryption
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(ui): create GroupDetailsPanel with full config editing
Side panel for editing group-level default configuration using AsidePanel.
Includes General, SSH, Telnet, Advanced, Mosh, and Appearance sections
with sub-panel navigation for Proxy, Chain, EnvVars, and Theme selection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(vault): wire GroupDetailsPanel, replace rename dialog with full config panel
Replace all group rename dialog triggers with the new GroupDetailsPanel sidebar.
The hover edit button, context menu, and tree view edit callbacks now open the
full group configuration panel instead of a simple rename dialog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(connect): apply group config defaults at connection time
When connecting to a host, merge group-level default configuration so
hosts inherit their group's settings for auth, protocol, appearance,
and other inheritable fields. Connection logs still reference the
original host's label/hostname.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(sync): include groupConfigs in sync and export payloads
Add groupConfigs to SyncPayload, SyncableVaultData, buildSyncPayload,
and applySyncPayload so group connection defaults are preserved during
cloud sync and data import/export. Also wire groupConfigs into the
vault object in SettingsPage so it flows through to the sync payload
builder.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(vault): update group configs on move and delete
* feat(host-panel): show inherited group defaults as placeholders
When editing a host that belongs to a group with configuration, group
default values now appear as placeholder text in username, startup
command, and charset fields where the host doesn't have its own value.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: clean up unused imports in GroupDetailsPanel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(group-panel): add/remove protocol sections, editable parent group
- SSH and Telnet sections are now add/remove — click "Add Protocol"
to enable, "..." menu to remove. Only enabled protocols override hosts.
- Parent Group is now editable via Combobox dropdown for quick
group moving.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: move SSH-specific fields into SSH protocol section
Startup Command, Legacy Algorithms, Proxy, Host Chaining,
Environment Variables, and Mosh are all SSH-specific and now only
visible when SSH protocol is added. Only Charset remains as a
shared field in the Advanced section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: hide charset and appearance when no protocol is added
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: close Add Protocol dropdown after selection
Use controlled open state to explicitly close the dropdown when a
protocol is selected, preventing residual content from overlapping
the newly rendered section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: apply group defaults in TerminalLayer sessionHostsMap
Terminal component was re-reading the original host from the hosts
array by hostId, bypassing the group defaults applied in
handleConnectToHost. Now sessionHostsMap applies resolveGroupDefaults
+ applyGroupDefaults when building the host object for each session,
so Terminal sees the merged credentials/settings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: move Add Protocol to bottom, fix i18n for protocol/font labels
- Add Protocol button moved below Appearance section
- Added i18n keys: addProtocol, removeProtocol, fontFamily, fontSize
- All hardcoded English strings replaced with t() calls
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: replace font family text input with TerminalFontSelect dropdown
Use the same font selector component as settings, showing available
terminal fonts with preview. Includes "Use Global" reset button.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(group-panel): match HostDetailsPanel key/certificate selection pattern
Replace the simple Combobox key selector with the same credential selection
flow used in HostDetailsPanel: a popover with Key/Certificate options,
inline combobox per type, and proper badge display with certificate icon.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(group-panel): add Local Key File option to credential selection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(group-panel): add identityFilePaths to GroupConfig and Local Key File option
- Added identityFilePaths to GroupConfig interface and INHERITABLE_KEYS
- GroupDetailsPanel now supports Key, Certificate, and Local Key File
credential selection, matching HostDetailsPanel's full credential flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prevent local key file input from overflowing panel width
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: constrain local key file input width with w-0 flex-1
Native input elements have a large default min-width. Using w-0 with
flex-1 forces the input to shrink within the flex container.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add overflow-hidden to SSH Card to contain local key file input
Matches HostDetailsPanel's Card which uses overflow-hidden on the
credentials section to prevent long file paths from overflowing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add min-w-0 to key file path row for proper text truncation
Flex children need min-w-0 for truncate to work correctly,
otherwise the text pushes the container wider.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: force key file path text truncation with inline max-width calc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use fixed 320px max-width on key file path text to force truncation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add overflow-hidden to AsidePanelContent to prevent content overflow
The root cause was the inner div of AsidePanelContent only had
overflow-x-hidden which was being overridden by ScrollArea's viewport.
Changed to full overflow-hidden with w-full box-border.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: override Radix ScrollArea viewport's display:table in AsidePanel
Radix ScrollArea Viewport wraps content in a div with
display:table and min-width:100%, causing content to expand beyond
the panel width. Override this on AsidePanelContent's ScrollArea
to use display:block and min-width:0 instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: critical issues — seed new hosts from group defaults, validate group names, fix empty import
- HostDetailsPanel: When groupDefaults has values for port/username/charset,
new hosts start with undefined/empty so group defaults take effect via
applyGroupDefaults() instead of being blocked by hardcoded values
- GroupDetailsPanel: Validate group name in handleSubmit to reject '/' and
'\' characters, matching the old rename dialog behavior, with visual error
- useVaultState: Check groupConfigs !== undefined instead of truthy so that
importing an empty array [] properly clears all group configs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: safe prefix replacement, remove dead code, extract shared resolveEffectiveHost
- Replace all .replace(oldPath, newPath) / .replace(sourcePath, newPath) with
explicit prefix slicing (newPath + str.slice(oldPath.length)) in handleSaveGroupConfig
and moveGroup for more robust path renaming
- Remove dead c.path === oldPath branch in finalConfigs mapping since updatedConfigs
already contains the config with newPath
- Extract resolveEffectiveHost helper in App.tsx to deduplicate group defaults
resolution in _handleTrayPanelConnect and handleConnectToHost
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: preserve undefined port on save when group has port default
form.port || 22 was forcing port to 22 even when intentionally left
undefined for group inheritance. Now uses nullish coalescing and only
defaults to 22 when no group port default exists.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: SSH-adjacent field detection, chain host defaults, telnet inheritance, theme clear
- hasSshFields() now checks proxyConfig, hostChain, startupCommand,
legacyAlgorithms, environmentVariables, moshEnabled, moshServerPath,
and identityFilePaths so the SSH section auto-opens when editing
- Chain hosts in sessionChainHostsMap now get group defaults applied
via resolveGroupDefaults + applyGroupDefaults
- Added telnetEnabled to GroupConfig interface and INHERITABLE_KEYS;
save handler sets telnetEnabled: true when Telnet section is on
- Theme/font "Use global" clear now sets override to false instead of
undefined, preventing parent group theme from leaking through
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: review round 4 — sync, SFTP, port forwarding, type safety, UX
- Scan groupConfigs in encrypted credential guard (P1 security)
- Add groupConfigs to auto-sync payload and three-way merge (P1 sync)
- Apply group defaults in SFTP connections (P1 SFTP)
- Apply group defaults in all port forwarding paths (P1 port forwarding)
- Make Host.port optional to fix unsafe type cast (P1 type safety)
- Fix port input empty → 0 instead of undefined (P2)
- Add port placeholder showing inherited value (P2)
- Mutual exclusion of group/host detail panels (P2)
- Fix sub-panel width jump 420px → 380px (P2)
- Validate duplicate group path on rename/reparent (P2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: review round 5 — null guard, empty array inheritance, memo comparator, form reset
- Guard groupConfigs import against null payload (P1 crash)
- Validate duplicate path on moveGroup drag-drop (P2 data corruption)
- Clear empty environmentVariables to undefined for group inheritance (P1)
- Clear empty hostChain to undefined for group inheritance (P2)
- Add groupConfigs to SftpView memo comparator (P1 stale defaults)
- Add key={editingGroupPath} to GroupDetailsPanel for form reset (P1)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: review round 6 — copy credentials, protocol dialog use effective host
- Apply group defaults in handleCopyCredentials (P2)
- Apply group defaults in hasMultipleProtocols check (P2)
- Pass effective host to ProtocolSelectDialog (P2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: serialize protocol:'ssh' marker to persist SSH section in group config
- Add protocol:'ssh' as marker field in handleSubmit SSH block
- Detect protocol:'ssh' in hasSshFields() to preserve section on reopen
- Clean up protocol field in removeSsh()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prevent key file path from overflowing host details panel
Add min-w-0 to flex containers and flex items displaying key file
paths. Without this, flex items default to min-width: auto which
prevents truncate from working and causes long file paths (e.g.
from the file picker) to blow out the panel width.
Closes#551
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add overflow-hidden to AsidePanel to prevent content overflow
The root cause of key file paths overflowing the panel was the
AsidePanel container itself lacking overflow-hidden. Even though
inner elements had min-w-0 and truncate, the absolute-positioned
panel div allowed content to visually escape its bounds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add overflow-hidden to credentials Card and key path row
Ensure truncation works by adding overflow-hidden at multiple
levels: the Port & Credentials Card container and each key file
path flex row.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use w-0 flex-1 to force key file path truncation
min-w-0 alone is insufficient in nested flex layouts. Setting w-0
with flex-1 forces the element to start at zero width and only grow
to fill available space, guaranteeing truncation works.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Increase HostDetailsPanel width from 380px to 420px to give more
room for inner content blocks
- Add max-w-full to AsidePanel/AsidePanelStack root so the panel
never exceeds its parent container width
- Add min-w-0 to ScrollArea and inner content div in AsidePanelContent
to allow flex children to shrink properly
- Use overflow-x-hidden instead of overflow-hidden to preserve
vertical layout flexibility
Closes#551
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add click-to-preview for images in AI chat
Uploaded images in AI chat messages can now be clicked to open a
full-size lightbox preview. Clicking the overlay or the image again
dismisses it. Uses the existing Radix Dialog component.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: use standard dialog style for image preview with close button
Replace transparent borderless overlay with proper windowed dialog that
has a background, border, and the built-in close button (X) in the
top-right corner. Remove focus ring that caused the blue border.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add title bar with filename and blurred backdrop to image preview
- Show filename in dialog header with border separator
- Add overlayClassName prop to DialogContent for per-instance overlay
customization (e.g. backdrop blur, custom background)
- Apply semi-transparent black background with backdrop-blur on overlay
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align title and close button vertically in image preview
Adjust header padding and close button position so the filename and
X button sit on the same visual line.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>