Pointermove used to setTreeWidth every pixel and re-render NotesManager
including MDXEditor. Apply live width via rAF + aside.style, commit React
state and storage only on pointerup.
Radix ScrollArea's display:table viewport child blocked flex shrink, so
narrowing the notes tree clipped long titles mid-glyph. Constrain the
tree width chain and ellipsize labels so content adapts with the panel.
* fix(notes): domain paste policy + Codex review follow-ups
Move clipboard paste resolution to domain/notes (pure, no React) and
address post-#2868 review items: structured text/plain wins over
wrapper HTML, caret-safe insert for long pastes (no EOF append),
https/protocol-relative image src, entity decode, quote-aware img
attrs, code-region protection, nested HTML islands, hard-break
preservation, height-only image CSS, and complete small-icon widths.
* fix: address Codex review on PR #2869
* perf(notes): cut hover re-renders and heavy image :has() CSS
Reduce perceived lag in large notes by (1) coalescing link-hover
setState with equality checks and rAF, (2) replacing the combinatorial
:has(img[width]) matrix with data-note-img-size markers plus lazy
images, and (3) merging DOM decoration into one observer that debounces
in edit mode so Lexical keystrokes do not re-walk anchors every frame.
* perf(notes): render preview with Streamdown instead of MDXEditor
Vault note preview no longer mounts Lexical/MDXEditor/CodeMirror for
read-only viewing. Preview uses static Streamdown (same stack as chat)
with HTML islands for centered heroes and sized images; edit mode keeps
MDXEditor. This cuts large-note scroll and mode-switch cost while
preserving host-link click handling and compact badge image layout.
* fix(notes): render markdown inside center HTML for Streamdown preview
Streamdown/remark does not parse Markdown nested in HTML blocks, so
README-style <div align="center"> heroes showed raw # ** and badge
syntax. Preprocess preview markdown: expand centered islands via marked
GFM to HTML, and rewrite relative images so Streamdown no longer shows
"Image blocked" chips under app CSP.
* fix(notes): GitHub-style preview with react-markdown
Replace Streamdown note preview (chat-oriented Image blocked chrome and
poor README layout) with react-markdown + remark-gfm + rehype-raw/sanitize
and github-markdown-css. Center HTML islands still expand via marked so
pasted heroes render; relative images and empty links are dropped quietly
instead of alt-as-title or blocked chips.
* fix(notes): keep relative images; map Vite public/ to site root
Stop dropping non-https note images. Preserve absolute and relative src
as-is, except rewrite repo public/ assets to site-root paths
(public/icon.png → /icon.png) so Vite/Electron serve them without
/public/* warnings. Unwrap oversized center blocks so Features-style
body stays left-aligned in preview like edit mode.
* fix(notes): left-align preview body; center only README hero
Force markdown-body text-align left and direct block headings/lists left.
After the first hero thematic break, strip align=center / text-align:center
so Catty/Features sections no longer inherit full-width centering. Tighten
oversized-center heuristics (Catty Agent, lists, blockquotes) without
treating the Netcatty logo title as a body section.
* fix(notes): force preview body left-align (nuclear CSS + unwrap)
Catty/Features still rendered centered because small align=center shells
and inheritance beat partial CSS. Force every node left via * selector,
re-enable center only under [align=center], unwrap all center shells after
the hero --- band, and set inline textAlign left on the preview root.
* fix(notes): stop /public asset requests; fix preview center inheritance
Root cause of body still centering: CSS re-applied text-align:center to
all descendants of [align=center], so one large shell centered Catty/lists.
Keep * left; only the shell itself may center (inline badges).
Root cause of Vite /public/* warnings: stored notes still had /public/...
paths and MDX loaded them raw. normalizeNotePublicAssetPaths rewrites
/public/ and public/ to site-root paths for both edit and preview display.
* revert(notes): restore MDXEditor for preview (drop GitHub markdown path)
GitHub-style react-markdown preview caused layout and asset-path issues
that diverged from edit mode. Preview again uses MDXEditor with
readOnly + mode key remount, matching the pre-Streamdown/GitHub path.
Keep display-time public/ → / rewrite so Vite does not request /public/*.
* perf(notes): reuse MDX editor when switching notes
Remove key={noteId} remount that tore down Lexical/CodeMirror on every
note change. Pass noteId and call setMarkdown on switch instead, after
the existing draft flush, so selection stays snappy and only document
content is replaced.
* perf(notes): defer setMarkdown on switch so selection paints first
Yield a double rAF before Lexical import on note change, show a
blocking overlay for large notes, and ignore external sync while a
swap is in flight. Hardens flaky Suspense loading assertions in SSR tests.
* fix(notes): align task-list checkboxes and make them clickable in preview
Center GFM checkboxes on the first text line, give them a solid hit target,
and toggle markdown from preview clicks since Lexical readOnly blocks
CheckListPlugin. Pure toggle helper lives in domain/notes/taskList.
* fix(notes): close multi-agent review findings on switch/paste/tasks
Gate stale onChange during deferred note-switch import, keep draft refs
in display-markdown space, baseline paste settle on live getMarkdown to
avoid false success after muted setMarkdown, mask code regions in task
toggles, and drop abandoned GitHub/Streamdown preview module and deps.
* fix(notes): address remaining Codex review findings
Clear Lexical undo history on note switch, always inert-block during
deferred setMarkdown, re-annotate host links and image sizes on attribute
mutations, harden entity/code-region paste paths, and angle-bracket image
destinations that contain spaces.
* fix(notes): tighten Codex follow-ups on src, code masks, tasks
Prefer real img src over data-src, normalize public/ paths only outside
code, mask indented/blockquote fences, and accept 1) ordered task markers.
* fix(notes): keep swap payloads fresh; keep nested list tasks
Refresh deferred setMarkdown when the same note's value changes mid-yield,
and stop treating nested list lines as indented code so task toggles stay
aligned with the preview DOM.
* test(notes): update swap guard source assertions
* fix(notes): collision-free code masks, real href, cancel hover rAF
Use a document-unique mask sentinel, match anchor href with attribute
boundaries (not data-href), and cancel the pending link-hover frame on
mouse leave so chips cannot reappear after exit.
* fix(notes): accept longer closing fences in code masks
CommonMark allows closing fences with more backticks/tildes than the
opener; line-scan masking now matches length >= open so public/ paths and
task samples inside those blocks stay protected.
* fix(notes): harden CommonMark fence, span, and indented-code masks
Use homogeneous fence openers, multi-backtick inline spans, and list-context
aware indented-code detection so samples stay masked without breaking nested
task indexing.
* fix(notes): task spacing, angled image dests, deep blockquote fences
Require whitespace after task brackets, parse <angled> image destinations
with spaces, and mask fenced code at any blockquote depth.
* fix(notes): raw-text HTML elements and HTML comment task masks
Locate script/style closers without nested-tag walks so raw bodies with
'<' are removed, and mask HTML comments before task indexing so preview
toggles hit visible checkboxes only.
* perf(notes): drop fade animation on note switch and editor load
Use an instant solid cover instead of opacity/transform fade-in so Lexical
swaps do not pay extra composite cost that can feel more janky than a hard cut.
---------
Co-authored-by: netcatty-bot <308658023+netcatty-bot@users.noreply.github.com>
* Cursor: Apply local changes for cloud agent
* fix(terminal): stop first SSH fail under StrictMode remount
Defer backend boot past the double-invoke, cancel superseded vault init,
gate start on vault hydration, and harden hotkey/attach cleanup so the
first connect no longer races a stale close or empty keychain.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* perf: finish StrictMode mega-subscriber plan (accent isolation + docs)
Stabilize published terminal themes across custom-accent drag, apply accent
at chrome/log leaves and CSS injection only, land Phase 0 design/plan docs,
and lock Host-bag / Bugbot-clean contracts for the isolation checklist.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* docs: add StrictMode mega-subscriber design and plan artifacts
Force-add Phase 0 spec/plan under docs/superpowers (gitignored locally)
so the delivery branch carries the approved design and plan copy.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: clear remaining StrictMode doubles and notes editor jank
Gate every AI send and reject concurrent runTurn, latch tray connect flush
and settings persistMounted remounts, and stop notes content drafts from
rebuilding MDXEditor/shell on each keystroke.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* perf: gate hidden notes panel subscribe and skip host-link annotate on keystrokes
Hidden terminal notes mounts keep a live snapshot read but stop listening to
publishes. Host-link DOM annotation follows hosts/mode (preview MutationObserver)
instead of every markdown value change.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: latch SSH transport idle TTL notify under StrictMode
Avoid duplicate main-process notifySettingsChanged on effect double-invoke
when the idle TTL value has not changed.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* perf: freeze inactive vault notes against hosts churn and prefetch MDXEditor
Gate the retained VaultNotesSection notesStore subscription while hidden,
ignore hosts identity in its memo compare when inactive, and warm the lazy
InlineMarkdownEditor chunk when notes become active.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: clear remaining AI StrictMode double-fire hazards
Latch Ask-AI terminal selection on requestId before draft mutation, hoist
Codex App Server IPC bridge to App (ref-counted), and make Catty
requestApproval join an existing waiter for the same toolCallId.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: remount-safe AI send latch and skip stale MCP approval IPC
Replace the component sendInFlightRef with a module key latch so StrictMode
panel remount cannot reopen a mid-flight send, and no-op resolveApproval when
the pending entry is already drained.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* test: allow gated useNotesStore in VaultNotesSection memo contract
The retained notes section now passes enabled:isActive so hidden mounts do
not subscribe; update the VaultView memo source assertion accordingly.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: keep note drafts across mode toggle and external note publishes
Flush ref-only content before edit/preview remount, and skip prop→editor
setMarkdown while a local draft diverges from the last synced value.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* chore: strip StrictMode sermon comments after Bugbot/Comment Sicko pass
Keep effect-order and public-export contracts; drop remount/perf narration
that the latches and tests already encode.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix: remount-safe Ask-AI terminal selection latch
Move consumed requestIds to a module Set so StrictMode remount cannot
append the same selection chip twice before parent state clears.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* chore: drop remount latch comment
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): protect title IME, surface persist failures, harden paste
Note titles rewrote controlled value during CJK composition (Sogou Wubi).
Notes localStorage writes ignored QuotaExceededError so restarts lost
content. Markdown paste intercept could preventDefault with a no-op insert
(fixes#2783).
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): commit title draft on blur for IME composition
IME-only title entry never called onCommit until compositionend; blur
could flush an empty draftTitle and then adopt the stale external value.
Commit the local draft on blur before parent flushNoteDraft.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): stash IME title drafts in refs without controlled rewrite
Composition updates now live-stash into draftTitleRef for teardown and
note-switch flushes, while onCommit still waits for idle IME. Blur syncs
superseded external titles before flush so stale drafts cannot overwrite.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): clear stashed title when IME composition is superseded
compositionEnd now live-stashes the adopted external title so pagehide
and note-switch flushes cannot persist rejected composed text.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): cancel draft flush during IME title stash
Prior idle commits could still debounce-flush mid-composition and rewrite
the controlled title. Stash now clears the timer, and external supersede
in the adopt effect live-stashes the authoritative title.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
* fix(notes): dedupe quota failure toasts during autosave
Debounced draft flushes can hit QuotaExceededError repeatedly; rate-limit
the error toast so users are not spammed every 300ms.
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: 陈大猫 <binaricat@users.noreply.github.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.
* feat(ai): unify capability registry, harness tools, and agent runtime
Centralize MCP/CLI/Catty tool surfaces in the capability catalog with codegen,
move Catty-only harness tools into catalog with local execution, converge
mcpServerBridge builtin dispatch via registry, and wire AgentRuntime with
vault bridge, permission grants, and Round 2 catalog parity.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(ai): vault host tools, artifact cards, and external agent vault guidance
Unify vault host create/import with capability RPC routing, add chat artifact cards with Vault navigation, and align external MCP agents with Catty rules so host creation never falls back to Vault Notes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): address Bugbot findings for streaming, vault bridge, and grants
Clear Catty streaming by chat sessionId, read latest vault state via getters on agent mutations, and stack grant persisters so unmounting one AI panel does not break others.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(vault): clear pending openNoteId after chat navigation focus
NotesManager notifies VaultView once the one-shot openNoteId request is applied so the id does not stick for the rest of the session.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(vault): serialize vault agent IPC mutations to prevent lost writes
Queue vault agent bridge handlers so concurrent hosts.create or note.create tool calls cannot overwrite each other via stale read-modify-write snapshots.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(vault): keep synchronous vault snapshot for agent IPC reads
Mirror hosts, notes, and groups in a ref updated immediately on agent writes so serialized mutations see the latest state before React re-renders.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): sync Catty MCP sessions and defer runtime cleanup on delete
Register terminal sessions with the main process before Catty tool calls, and clear ToolOutputStore only after stopping an active turn when deleting a chat session.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): avoid deleting a newer abort controller on late stop
Only remove the session abort-controller entry when stop finishes if it still refers to the controller that was stopped.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): sync vault snapshot on render and await active turn on stop
Refresh vault agent reads when React props change without clobbering in-flight agent writes, and wait for runTurn to finish before clearing ToolOutputStore on session delete.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): reload permission grants from storage before auto-approve
Re-read persisted grant rules on each approval check so revocations in Settings take effect without requiring a chat hook remount.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ai): sync permission grants to main process immediately on write
Push MCP permissionGrantsSnapshot as soon as Always allow persists a rule so the next tool call in the same agent turn sees the grant without waiting for useEffect.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(notes): allow clearing the default note title while editing
Stop sanitizeNoteTitle from forcing empty titles back to "Untitled note", so users can delete the placeholder and type a custom title.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(notes): allow clearing titles from tree rename
Remove the empty-title guard in sidebar tree rename so it matches the main title editor behavior.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep sortedNotesRef in sync on render and route note mutations through commitNotes so imports merge against the latest vault state.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add folder context-menu import, show copy buttons only in preview, tighten code block spacing, and fix language selector/dropdown sizing.
Co-authored-by: Cursor <cursoragent@cursor.com>
Read Markdown payloads first, then merge against the latest note list so in-flight edits are not overwritten during import.
Co-authored-by: Cursor <cursoragent@cursor.com>
Skip fenced code blocks when deriving import titles, guard concurrent imports with a ref, and tighten the file picker accept list.
Co-authored-by: Cursor <cursoragent@cursor.com>
Support bulk .md/.txt import into the current notebook folder and add hover copy buttons on note code blocks in edit and preview modes.
Co-authored-by: Cursor <cursoragent@cursor.com>