Skip to content

v2.1.9 - UX Tweaks. - #59

Merged
ddutchie merged 14 commits into
mainfrom
ddutchie/uxtweaks
Jun 22, 2026
Merged

ddutchie merged 14 commits into
mainfrom
ddutchie/uxtweaks

Conversation

@ddutchie

@ddutchie ddutchie commented Jun 22, 2026 •

Copy link
Copy Markdown
Owner

What does this PR do?

v2.1.9 — a broad UX/UI quality release spanning the Knowledge Graph, Idea Flow, Settings, Kanban board, and the AI chat tool-call pipeline. The headline fix is a root-cause repair for SSE tool-call argument assembly (which was silently dropping streamed fragments at reader.read() boundaries, causing truncated note titles and malformed tool calls). Beyond that, the release adds markdown rendering on board cards, a restructured graph detail panel with links/linked-tasks/linked-notes sections, comprehensive accessibility and hardcoded-colour audits across Settings/Idea Flow/Knowledge Graph, and numerous polish fixes across all views.

Type of change

  • Bug fix
  • New feature
  • Refactor / code quality
  • Docs / changelog
  • Tests

Screenshots / recording

Checklist

  • npm run type-check:all passes
  • npm run lint passes
  • npm test passes (569 tests, 31 files — includes electron/pipeline-regression.test.ts)
  • npm run test:e2e passes (run before merging UI changes or cutting a release)
  • No hardcoded colours — CSS variables only (var(--accent), var(--text-primary), etc.)
  • No text-[Npx] pixel font classes — rem equivalents only (text-[0.714rem], text-xs, etc.)
  • New IPC handlers wrapped in handle() and return IpcResult<T> (no new IPC handlers)
  • New DB migrations appended (not edited) in schema.ts (no new migrations)
  • New SQL goes in electron/db/queries.ts — single source of truth (no new SQL)
  • New dependencies or devDependencies added to ROLE_MAP in scripts/generate-licenses.js and licenses.json regenerated (no new deps)
  • New MCP tools registered in electron/mcp/tools/index.ts dispatch + electron/lib/tool-schemas.ts Zod schema (no new MCP tools)
  • If you added/removed a --external:<pkg> flag in the compile script: updated the appropriate allowlist group (no compile changes)

Notes for reviewer

Critical fix — SSE assembly pipeline:
The most important change is the SSE tool-call assembly fix (electron/lib/sse.ts + changes in electron/ipc/chat.ts and electron/lib/pi-agent-loop.ts). This was diagnosed after a proxy-team investigation confirmed the OpenAI proxy was a clean passthrough — the corruption was in Cairn's SSE line-splitting. The new iterSseData() async generator uses a carry buffer to survive chunk boundaries. Regression tests in electron/pipeline-regression.test.ts cover 12 arbitrary boundary positions.

Dev-only trace logging:
electron/lib/tool-trace.ts logs SHA-256/length/head-tail at sse-args, parse, and lookup stages. Active only when CAIRN_TOOL_TRACE=1 or NODE_ENV=development. Zero output in packaged builds — safe to leave in.

Key areas to review:

  1. SSE assembly (electron/lib/sse.ts, electron/ipc/chat.ts:171-217, electron/lib/pi-agent-loop.ts:546-602) — verify the carry-buffer logic handles [DONE] termination and final unterminated lines correctly.
  2. Malformed-arg guard (electron/ipc/chat.ts:247-276, electron/lib/pi-agent-loop.ts:654-685) — verify the error message is pushed back to the model as a tool-role message so the model can recover.
  3. ensure_note normalization (electron/shared/text-utils.ts:34-37, electron/mcp/tools/notes.ts:46-48, electron/ipc/chat-executor.ts:147-150) — verify both lookup sites use the same normalizeNoteTitle() predicate (this was a previous source of duplicate-note bugs when they disagreed).
  4. Graph detail panel restructure (src/components/graph/GraphDetailPanel.tsx) — flex-column layout with pinned collapsible sections. Verify the link/task/note navigation works and sections expand/collapse correctly.
  5. Markdown on board cards — verify the "More"/"Less" toggle, scroll reset on collapse, and that all markdown features (tables, callouts, math, wikilinks) render in the compact card context.
  6. DatePicker portal fix (src/components/ui/DialogContent.tsx, src/globals.css) — verify the date picker no longer dismisses the parent dialog when clicked, and that onInteractOutside correctly identifies portaled children.

Summary by CodeRabbit

  • New Features
    • Full markdown rendering for kanban card descriptions (expandable/scrollable), plus improved card/tag UI.
    • Knowledge graph “Links” panel with wikilinks + semantic connections, and “Linked tasks/notes”.
    • Card deep-linking (“Open in Board”) and expanded graph search (snippets + tag matching).
    • Graph zoom controls; tag filter includes a one-click clear option.
  • Bug Fixes
    • Improved SSE/tool-call streaming and validation to prevent truncated or malformed tool arguments.
    • Standardized note title matching for reliable note de-duplication.
    • Fixed portal/date-picker/dialog interaction and improved graph empty/error states.
  • Accessibility
    • Added/expanded ARIA labels and focus-visible styling across action buttons and toggles.

ddutchie added 11 commits June 22, 2026 14:18
…fix (v2.1.9)

- Fix date picker portal clicks dismissing dialogs (pointer-events + onInteractOutside)
- Render kanban card descriptions as markdown with expand/collapse
- Move card tags to bottom, add xs badge size variant
- Add wikilinks + semantic links sections to graph detail panel
- Add linked tasks / linked notes collapsible sections to graph detail panel
- Deep-link 'Open in Board' to scroll-to-column + open-card-detail
- Switch 5 components to shared NoteMarkdownPreview (MDPreviewPanel, AboutSection, board archive, card-detail, AiSummaryNode)
- Increase graph snippet length 120 → 600 chars
- Add changelog v2.1.9
…, dbl-click position, ai_summary edit, swatch colors
… edge legend, persistence, tag links, breadcrumb
…es, escape close, type-to-confirm, export error
- Add carry-buffer SSE line parser (electron/lib/sse.ts) so data: records
  straddling reader.read() chunk boundaries are no longer dropped — the
  root cause of truncated note titles and malformed tool-call JSON.
- Both chat loop (electron/ipc/chat.ts) and coding agent loop
  (electron/lib/pi-agent-loop.ts) replaced their split('\n') anti-pattern
  with iterSseData(); both now surface JSON.parse errors to the model
  instead of silently substituting args={} and executing the tool.
- ensure_note title matching now uses normalizeNoteTitle() (trim +
  collapse internal whitespace) at both lookup sites (notes.ts and
  chat-executor.ts wrapper), so whitespace-variant titles dedup. Wrapper
  and canonical path no longer risk disagreeing on the match key.
- Dev-gated trace logging (electron/lib/tool-trace.ts) at sse-args,
  parse, and lookup stages — active only when CAIRN_TOOL_TRACE=1 or
  NODE_ENV=development; logs lengths + short SHA-256 + head/tail only.
- Regression tests (electron/pipeline-regression.test.ts) cover
  chunk-boundary reassembly and title normalization.
@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d9e3432d-4379-4e89-a84a-c309488279e5

📥 Commits

Reviewing files that changed from the base of the PR and between f3c354a and b678c44.

📒 Files selected for processing (2)
  • electron/main.ts
  • electron/splash/bootsplash.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • electron/main.ts
  • electron/splash/bootsplash.ts

📝 Walkthrough

Walkthrough

v2.1.9 introduces a shared iterSseData SSE parser that preserves data: fragments across chunk boundaries, with traceTool debugging and normalizeNoteTitle for idempotent note lookup. The frontend gains a redesigned Knowledge Graph detail panel with wikilink/semantic link sections and deep-link navigation, collapsible markdown-rendered Kanban cards, Idea Flow node/edge fixes, a portal pointer-events fix for Radix dialogs, broad NoteMarkdownPreview adoption, and a Settings accessibility/theming/safety audit.

Changes

SSE Tool-Call Pipeline Fix

Layer / File(s) Summary
SSE parser, tool tracer, title normalizer
electron/lib/sse.ts, electron/lib/tool-trace.ts, electron/shared/text-utils.ts
iterSseData carry-buffers incomplete lines across reader.read() chunk boundaries; traceTool emits dev-only compact hash/snippet logs; normalizeNoteTitle trims and collapses whitespace for deduplication keys.
SSE integration in chat, agent loop, and MCP notes tool
electron/ipc/chat.ts, electron/lib/pi-agent-loop.ts, electron/ipc/chat-executor.ts, electron/mcp/tools/notes.ts
Both streaming paths replace manual TextDecoder loops with for await over iterSseData; malformed tool-call JSON now emits a descriptive tool-role error instead of executing with {}; ensure_note lookup uses normalized title comparison.
Pipeline regression tests
electron/pipeline-regression.test.ts
Regression suite covers iterSseData chunk-boundary safety (arbitrary splits, single-byte, unterminated), normalizeNoteTitle whitespace/type/dedup semantics, and a stripMarkdown sanity check.

Frontend UI: Graph, Kanban, Flow, Notes, Settings

Layer / File(s) Summary
Portaled popover pointer-events and dialog dismiss fix
src/app/globals.css, src/components/ui/dialog.tsx, src/components/ui/date-picker.tsx, src/components/notes/WikilinkPicker.tsx
Adds [data-dialog-portal] { pointer-events: auto } CSS rule; DialogContent intercepts onInteractOutside to prevent dismissal when the event target is inside a [data-dialog-portal] ancestor; date-picker and WikilinkPicker portal roots are marked.
Badge xs variant, CairnEvents.filterByTag, SettingsRow/Toggle accessibility
src/components/ui/badge.tsx, src/lib/events.ts, src/components/settings/shared.tsx
Badge gains a size prop with "xs" variant; CairnEvents.filterByTag(tagId) dispatches cairn:filter-by-tag; SettingsRow injects generated id into child and uses htmlFor; Toggle applies aria-label from label prop.
Knowledge Graph detail panel: links section and deep-link navigation
src/components/graph/GraphDetailPanel.tsx, electron/db/graph-queries.ts
Panel computes wikiLinks/semanticLinks/linkedTaskItems/linkedNoteItems and renders them in CollapsibleLinkSection blocks; card navigation dispatches scrollToColumn and openCard via requestAnimationFrame; tag nodes get a "Browse tagged notes" CTA; snippet truncation increased to 600 characters.
Knowledge Graph: view, force canvas, radial canvas
src/components/graph/KnowledgeGraphView.tsx, src/components/graph/ForceGraphCanvas.tsx, src/components/graph/RadialTreeCanvas.tsx
KnowledgeGraphView persists preferences to localStorage, adds tag-aware search, live recompute timer, threshold-aware edge counts, FilteredEmptyState, and two-part edge+node legend; ForceGraphCanvas adds onLinkClick for semantic edges; RadialTreeCanvas adds clickable semantic cross-edges, tag-sibling glow, and D3 zoom controls.
Kanban: collapsible markdown cards, tooltip actions, board/column UI
src/components/kanban/card.tsx, src/components/kanban/card-detail.tsx, src/components/kanban/board.tsx, src/components/kanban/column.tsx
KanbanCard gains collapsible NoteMarkdownPreview description, assignee footer, and +N tag tooltip; CardDetailModal replaces MarkdownContent with NoteMarkdownPreview and wraps linked-note actions in Tooltip; board tab shows non-zero card count badge; WIP counter becomes a clickable dialog trigger.
Idea Flow: node fixes, edge deletion undo, empty state, color/accessibility
src/components/flow/flow-view.tsx, src/components/flow/edges/FlowEdge.tsx, src/components/flow/NodeEditModal.tsx, src/components/flow/nodes/...
Edit-modal guards corrected for ai_summary vs group nodes; node placement uses screenToFlowPosition; empty-state Lightbulb panel added; FlowEdge uses deleteElements for undo; NodeEditModal uses CSS-variable swatches; GroupNode uses explicit borderIdle; AiSummaryNode renders via NoteMarkdownPreview; NoteRefNode/TaskRefNode distinguish dangling state.
Notes view, NoteMarkdownPreview adoption, sidebar counts
src/components/notes/notes-view.tsx, src/components/notes/notes-view/NoteListItem.tsx, src/components/notes/MDPreviewPanel.tsx, src/components/settings/AboutSection.tsx, src/components/layout/sidebar.tsx, src/components/layout/project-overview/useProjectMetrics.ts
Notes view adds filter-clear X button and cairn:filter-by-tag listener; NoteListItem shows up to 3 tag Badge items; MDPreviewPanel and AboutSection replace react-markdown pipelines with NoteMarkdownPreview; sidebar shows per-project open-card-count badge; pinned notes capped at 4.
Settings: accessibility, theming, dynamic shortcuts, safety hardening
src/components/settings/AISettings.tsx, src/components/settings/DataSettings.tsx, src/components/settings/GeneralSettings.tsx, src/components/settings/ShortcutsSettings.tsx, src/components/settings/TagsSettings.tsx, src/components/settings/ViewVisibilitySettings.tsx, src/components/settings/...
AISettings adds label props to Toggles, removes context-limit clamping, adds Custom option, replaces hardcoded colors with CSS variables; DataSettings wraps export in try/catch with error UI and requires typing DELETE for reset; GeneralSettings adds Escape-to-revert; ShortcutsSettings generates nav shortcuts dynamically from hiddenViews; TagsSettings refactors palette to {color,name} objects with Escape-to-close and aria-label; ViewVisibilitySettings adds ariaLabel prop with themed toggle thumb.
Splash and boot sequence improvements
electron/main.ts, electron/splash/boot-sequence.ts, electron/splash/bootsplash.ts
main.ts adds early splash progress events for pre-boot workflow; boot-sequence.ts emits initial migrations progress and adds final notes-sync progress before completion; bootsplash.ts removes ambient progress cycling and relies solely on real boot events.
v2.1.9 release notes
changelogs/v2.1.9.md
Documents all features, fixes, and polish items in this release.

Sequence Diagram(s)

sequenceDiagram
  participant Provider as AI Provider (SSE)
  participant iterSseData as iterSseData
  participant chat_ts as chat.ts / pi-agent-loop.ts
  participant traceTool as traceTool
  participant executeTool as executeTool / executeSingleTool

  Provider-->>iterSseData: ReadableStream Uint8Array chunks
  loop SSE frames (carry-buffered)
    iterSseData-->>chat_ts: yield data: payload string
    chat_ts->>chat_ts: accumulate contentBuffer / toolCallBuffers[index]
  end
  chat_ts->>traceTool: traceTool("sse-args", {name, args})
  chat_ts->>chat_ts: JSON.parse(tc.function.arguments)
  alt parse success
    chat_ts->>traceTool: traceTool("parse", {title, content, raw})
    chat_ts->>executeTool: executeTool(name, args)
    executeTool-->>chat_ts: result
  else parse failure
    chat_ts->>chat_ts: emitToolCall + emitToolCallDone (UI events)
    chat_ts->>chat_ts: push tool role message {error: parseError}
    chat_ts-->>Provider: continue to next tool call (no execution)
  end
Loading
sequenceDiagram
  participant User
  participant GraphDetailPanel
  participant CairnEvents
  participant notesView as notes-view.tsx
  participant board as board / KanbanColumn

  User->>GraphDetailPanel: click tag node "Browse tagged notes"
  GraphDetailPanel->>CairnEvents: dispatch filterByTag(tagId) via rAF
  GraphDetailPanel->>GraphDetailPanel: setView("notes"), close panel
  CairnEvents-->>notesView: cairn:filter-by-tag event
  notesView->>notesView: setActiveTagId(tagId)

  User->>GraphDetailPanel: click card node "Open in Board"
  GraphDetailPanel->>GraphDetailPanel: setView("board"), rAF
  GraphDetailPanel->>CairnEvents: dispatch scrollToColumn(columnId)
  GraphDetailPanel->>CairnEvents: dispatch openCard(cardId)
  CairnEvents-->>board: scroll column + open card detail modal
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • ddutchie/cairn#55: Both PRs modify electron/lib/pi-agent-loop.ts to adjust agent-loop streaming and tool-argument processing; this PR adds SSE chunk-boundary safety and explicit parse-error handling, while the related PR optimized JSON output formatting in the same subsystem.
  • ddutchie/cairn#57: Both PRs update the Electron splash/boot flow—this PR adjusts splash progress emission and removes ambient cycling in electron/main.ts and electron/splash/*, building on the boot-sequence architecture introduced in that PR.

Poem

🐇 Hop hop, the data stream flows,
No more lost chunks when the buffer grows!
Cards render markdown, graphs click and glow,
Tag filters fly with a custom event throw.
The rabbit ensures each title is trimmed—
v2.1.9 ships, and nothing is dimmed! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'v2.1.9 - UX Tweaks' is vague and overly broad, using generic phrasing that doesn't convey the primary technical or UX changes from the changeset. Revise the title to be more specific and descriptive of the main change. For example: 'v2.1.9: Fix SSE tool-call streaming and enhance Knowledge Graph/Kanban UI' or focus on the critical fix: 'Fix SSE chunk boundary handling in tool-call argument assembly'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ddutchie/uxtweaks

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed due to a network error.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Nitpick comments (1)
src/components/layout/sidebar.tsx (1)

219-223: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Precompute project card counts once instead of filtering inside each row.

Line 222 recalculates a full cards.filter(...) for every project render. Pre-aggregating counts once per render avoids repeated scans.

⚡ Suggested refactor
   const projects = React.useMemo(
     () => activeWorkspaceId ? getWorkspaceProjects(activeWorkspaceId) : [],
     // eslint-disable-next-line react-hooks/exhaustive-deps
     [activeWorkspaceId, workspaces],
   );
+  const openCardCountByProject = React.useMemo(() => {
+    const counts = new Map<string, number>();
+    for (const card of cards) {
+      if (card.archivedAt) continue;
+      counts.set(card.projectId, (counts.get(card.projectId) ?? 0) + 1);
+    }
+    return counts;
+  }, [cards]);
@@
-                openCardCount={cards.filter((c) => c.projectId === project.id && !c.archivedAt).length}
+                openCardCount={openCardCountByProject.get(project.id) ?? 0}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/sidebar.tsx` around lines 219 - 223, The openCardCount
calculation on line 222 performs a full cards.filter scan for every project in
the render loop, causing redundant iterations over the cards array. Create a
precomputed map object before rendering the project list that aggregates card
counts by projectId (filtering for non-archived cards), then replace the inline
cards.filter expression with a simple lookup into this map to retrieve the count
for each project.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/graph/GraphDetailPanel.tsx`:
- Line 383: The hover text color class at line 383 uses the raw Tailwind color
name `hover:text-white`, which violates the repository's color-token rule
requiring all colors to use CSS variables. Replace `hover:text-white` with an
appropriate CSS variable token such as `hover:text-[var(--text-primary)]` or
another semantic token that matches your design system to ensure consistency
with the styling guidelines.
- Around line 77-90: The semantic array is sorted by weight but does not
deduplicate entries by noteId, while the keyed rows use key={link.noteId}. This
can result in duplicate keys and unstable React rendering when multiple edges
reference the same note. After sorting the semantic array by weight, add
deduplication logic to ensure each noteId appears only once, keeping the
highest-weighted entry for each note. Apply the same deduplication pattern to
the other locations mentioned (also applies to lines 325-328).
- Around line 127-139: The current code uses a single requestAnimationFrame to
defer the dispatch of CairnEvents.scrollToColumn and CairnEvents.openCard
events, but this can execute before the destination useEffect listeners have
attached, causing events to be dropped. Wrap the entire existing
requestAnimationFrame callback (which contains the calls to window.dispatchEvent
for both CairnEvents.scrollToColumn and CairnEvents.openCard) inside another
requestAnimationFrame call to defer the event dispatch to a later animation
frame, ensuring the listeners are mounted and ready to receive the events.

In `@src/components/graph/KnowledgeGraphView.tsx`:
- Around line 66-73: The initialization of the spacing and semanticThreshold
states can receive NaN values when parseFloat processes invalid data, which will
corrupt the slider values and display. Add validation using isFinite() to both
state initializers to check that the parsed float values are valid finite
numbers. If a parsed value from localStorage is not finite, return the
corresponding default value instead (1.2 for spacing state and 1.0 for
semanticThreshold state). Apply this check after calling parseFloat but before
setting the state value.
- Around line 19-24: The EDGE_LEGEND constant initializes color values once
using resolveCssVar(), which resolves CSS variables to static values at startup.
When the theme changes at runtime, these cached color values become stale and
don't update. To fix this, either store the CSS variable strings directly in the
EDGE_LEGEND array (such as "var(--accent)" instead of the resolved color value)
and let the CSS system resolve them dynamically, or move the resolveCssVar()
calls from the EDGE_LEGEND initialization into the render or component logic
where they can be re-evaluated on each render when the theme changes.
- Around line 122-137: The current search matching logic in the filter operation
for matchingIds is inefficiently scanning tagMemberEdges for every node being
checked, creating O(nodes × edges) complexity. Pre-compute a Map that associates
each node ID with its array of connected tag IDs before the filter operation
begins (build it once from tagMemberEdges where e.source maps to node IDs), then
replace the tagMemberEdges.filter call inside the filter function with a simple
lookup in this pre-computed map to directly get the tag IDs for each node.

In `@src/components/kanban/card.tsx`:
- Around line 145-149: The status badge styling in the span className uses
slash-alpha syntax (bg-[var(--danger)]/10 and bg-[var(--warning)]/10) which
deviates from the repository standard. Replace these slash-alpha background
classes with the color-mix pattern following the guideline: use color-mix(in
srgb, var(--token) X%, transparent) for alpha variants instead. Apply this
change to both the "overdue" status styling (currently bg-[var(--danger)]/10)
and the "today" status styling (currently bg-[var(--warning)]/10) to maintain
consistency with the codebase conventions.

In `@src/components/kanban/column.tsx`:
- Line 347: The pluralization ternary operator in the archived cards display
text is ineffective because it returns an empty string in both branches. In the
section displaying the archived cards count using archivedCards.length, fix the
ternary conditional to return "s" when the length is not equal to 1 (for
plural), and an empty string when it equals 1 (for singular). This will properly
pluralize the word "archived" to "archiveds" when there are multiple archived
cards.

In `@src/components/notes/notes-view.tsx`:
- Around line 307-313: The button element that clears the filter (with
onClick={() => setFilter("")}) is missing an accessible name, which prevents
assistive technologies from announcing its purpose. Add an aria-label attribute
to this button with a descriptive label such as "Clear filter" or "Clear search"
to ensure screen readers can properly communicate the button's function to
users.

In `@src/components/settings/DataSettings.tsx`:
- Around line 99-108: The text input field in the delete confirmation section
lacks a programmatic label, making it inaccessible to screen reader users. Add
an explicit label for the input element by either creating a label element with
an id and associating it to the input via htmlFor attribute, or by adding an
aria-labelledby attribute to the input that references the id of the existing
instruction paragraph above it, or by adding an aria-label attribute directly to
the input with descriptive text such as "Type DELETE to confirm deletion".

In `@src/components/settings/MobileSettings.tsx`:
- Around line 126-133: The span element containing "Scan to connect" text uses
text-[10px] for font sizing, which violates the rem-based Tailwind class
requirement and prevents scaling with user font settings. Replace the
text-[10px] class on the span with an appropriate rem-based Tailwind text size
class (such as text-xs or text-sm) that will scale properly with the user's font
size preferences.

In `@src/components/settings/shared.tsx`:
- Around line 50-52: The id prop is being injected into the children element in
lines 50-52 but is not being forwarded to the actual switch/toggle element
rendered in lines 63-78, breaking the label htmlFor binding. Modify the code in
the lines 63-78 section (likely in the Toggle component or switch rendering) to
ensure the id prop is passed through and applied to the actual input element so
that the label can properly reference it via htmlFor.

In `@src/components/settings/TagsSettings.tsx`:
- Around line 10-23: The color palette array in TagsSettings.tsx contains
hardcoded hex color values that violate the coding guideline requiring all
colors to use CSS variables. Replace each hardcoded hex value in the color
property of the palette objects (the entries with "Indigo", "Violet", "Purple",
"Pink", "Rose", "Red", "Orange", "Yellow", "Green", "Teal", "Cyan", "Blue",
"Slate", and "Stone" names) with appropriate CSS variable references using the
var() function instead of the literal hex codes like "`#6366f1`", "`#8b5cf6`", etc.
- Around line 66-71: The color-swatch trigger button in TagsSettings.tsx lacks
proper accessibility for screen readers. Add an aria-label attribute to the
button element that contains the onClick handler with setColorPickerId. The
aria-label should provide a clear, descriptive action text (such as "Change
color" or "Change tag color") that screen readers will announce, ensuring the
button's purpose is accessible to all users. This should accompany or replace
the existing title attribute for proper screen reader support.

---

Nitpick comments:
In `@src/components/layout/sidebar.tsx`:
- Around line 219-223: The openCardCount calculation on line 222 performs a full
cards.filter scan for every project in the render loop, causing redundant
iterations over the cards array. Create a precomputed map object before
rendering the project list that aggregates card counts by projectId (filtering
for non-archived cards), then replace the inline cards.filter expression with a
simple lookup into this map to retrieve the count for each project.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 12e3e9db-5d83-4ffb-9b99-2f1e4acdc3a8

📥 Commits

Reviewing files that changed from the base of the PR and between 6229a69 and ab40b22.

📒 Files selected for processing (49)
  • changelogs/v2.1.9.md
  • electron/db/graph-queries.ts
  • electron/ipc/chat-executor.ts
  • electron/ipc/chat.ts
  • electron/lib/pi-agent-loop.ts
  • electron/lib/sse.ts
  • electron/lib/tool-trace.ts
  • electron/mcp/tools/notes.ts
  • electron/pipeline-regression.test.ts
  • electron/shared/text-utils.ts
  • src/app/globals.css
  • src/components/flow/NodeEditModal.tsx
  • src/components/flow/edges/FlowEdge.tsx
  • src/components/flow/flow-view.tsx
  • src/components/flow/nodes/AiSummaryNode.tsx
  • src/components/flow/nodes/GroupNode.tsx
  • src/components/flow/nodes/IdeaNode.tsx
  • src/components/flow/nodes/NoteRefNode.tsx
  • src/components/flow/nodes/TaskRefNode.tsx
  • src/components/flow/nodes/UrlNode.tsx
  • src/components/graph/ForceGraphCanvas.tsx
  • src/components/graph/GraphDetailPanel.tsx
  • src/components/graph/KnowledgeGraphView.tsx
  • src/components/graph/RadialTreeCanvas.tsx
  • src/components/kanban/board.tsx
  • src/components/kanban/card-detail.tsx
  • src/components/kanban/card.tsx
  • src/components/kanban/column.tsx
  • src/components/layout/project-overview/useProjectMetrics.ts
  • src/components/layout/sidebar.tsx
  • src/components/notes/MDPreviewPanel.tsx
  • src/components/notes/WikilinkPicker.tsx
  • src/components/notes/notes-view.tsx
  • src/components/notes/notes-view/NoteListItem.tsx
  • src/components/settings/AISettings.tsx
  • src/components/settings/AboutSection.tsx
  • src/components/settings/AgentSettings.tsx
  • src/components/settings/DataSettings.tsx
  • src/components/settings/EmbeddingsSettings.tsx
  • src/components/settings/GeneralSettings.tsx
  • src/components/settings/MobileSettings.tsx
  • src/components/settings/ShortcutsSettings.tsx
  • src/components/settings/TagsSettings.tsx
  • src/components/settings/ViewVisibilitySettings.tsx
  • src/components/settings/shared.tsx
  • src/components/ui/badge.tsx
  • src/components/ui/date-picker.tsx
  • src/components/ui/dialog.tsx
  • src/lib/events.ts

Comment thread src/components/graph/GraphDetailPanel.tsx Outdated
Comment thread src/components/graph/GraphDetailPanel.tsx
Comment thread src/components/graph/GraphDetailPanel.tsx Outdated
Comment thread src/components/graph/KnowledgeGraphView.tsx
Comment thread src/components/graph/KnowledgeGraphView.tsx
Comment thread src/components/settings/DataSettings.tsx
Comment thread src/components/settings/MobileSettings.tsx Outdated
Comment thread src/components/settings/shared.tsx
Comment thread src/components/settings/TagsSettings.tsx
Comment thread src/components/settings/TagsSettings.tsx
ddutchie added 2 commits June 22, 2026 15:55
- Remove MIN_SPLASH_MS=3000 artificial floor and splashOpenedAt timing
  from main.ts closeSplash(). Splash now closes as soon as the main
  window finishes loading + 200ms CSS transition.
- Remove the ambient cycle from bootsplash.ts (6 fake messages, 250ms
  interval, creep-toward-95% bar). Progress bar now only moves on real
  boot events.
- Add real progress events for pre-boot work in main.ts: Opening

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
electron/main.ts (1)

232-242: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Minor timing mismatch with CSS transition duration.

The 200ms delay is slightly shorter than the 300ms CSS transition defined in bootsplash.ts line 167 (transition: width 300ms ease). In practice, this is unlikely to be noticeable since the progress bar is probably already at 95%+ when "Ready" is sent, so the actual transition duration is shorter than 300ms.

If you want perfect timing alignment, consider using 300-350ms to account for the full transition plus any frame timing variations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/main.ts` around lines 232 - 242, The closeSplash function uses a
200ms setTimeout delay before showing the main window, but this is shorter than
the 300ms CSS transition duration defined in bootsplash.ts. To achieve perfect
timing alignment with the CSS transition and account for frame timing
variations, increase the setTimeout delay from 200ms to 300-350ms.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/main.ts`:
- Around line 202-214: The pre-boot progress events in main.ts are emitting
specific percentages (3% and 6%) that conflict with the boot-sequence.ts
progress events, causing the progress bar to reset to 0% when actual migrations
begin. Remove the pct property from both splash.progress() calls (the one with
label "Opening database…" and the one with label "Registering handlers…") to use
only the step and label properties, allowing boot-sequence.ts to own the full
0-100% progress range and prevent the backwards jump. Keep the step and label
values to provide user feedback without numerical progress values.

---

Nitpick comments:
In `@electron/main.ts`:
- Around line 232-242: The closeSplash function uses a 200ms setTimeout delay
before showing the main window, but this is shorter than the 300ms CSS
transition duration defined in bootsplash.ts. To achieve perfect timing
alignment with the CSS transition and account for frame timing variations,
increase the setTimeout delay from 200ms to 300-350ms.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ee5d55bd-a041-4a6b-a6ca-9efd24a06b2b

📥 Commits

Reviewing files that changed from the base of the PR and between ab40b22 and f3c354a.

📒 Files selected for processing (13)
  • electron/main.ts
  • electron/splash/boot-sequence.ts
  • electron/splash/bootsplash.ts
  • src/components/graph/GraphDetailPanel.tsx
  • src/components/graph/KnowledgeGraphView.tsx
  • src/components/kanban/card.tsx
  • src/components/kanban/column.tsx
  • src/components/layout/sidebar.tsx
  • src/components/notes/notes-view.tsx
  • src/components/settings/DataSettings.tsx
  • src/components/settings/MobileSettings.tsx
  • src/components/settings/TagsSettings.tsx
  • src/components/settings/shared.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/components/settings/MobileSettings.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/components/settings/TagsSettings.tsx
  • src/components/layout/sidebar.tsx
  • src/components/settings/shared.tsx
  • src/components/notes/notes-view.tsx
  • src/components/settings/DataSettings.tsx
  • src/components/graph/KnowledgeGraphView.tsx
  • src/components/graph/GraphDetailPanel.tsx
  • src/components/kanban/card.tsx

Comment thread electron/main.ts
Pre-boot progress events in main.ts (Opening database…, Registering
handlers…) were sending pct: 3 and pct: 6, but boot-sequence.ts starts
migrations at pct: 0 — causing the progress bar to jump backwards.

Removed pct from both pre-boot events so they only update the label.
Made SplashProgress.pct optional and updated the splash:progress handler
to only set the bar width when pct is explicitly provided, keeping the
current position otherwise.
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