Skip to content

Add a first-run onboarding card to OK Desktop. A dismissable checklist…#411

Merged
inkeep-oss-sync[bot] merged 6 commits into
mainfrom
copybara/sync
Jul 1, 2026
Merged

Add a first-run onboarding card to OK Desktop. A dismissable checklist…#411
inkeep-oss-sync[bot] merged 6 commits into
mainfrom
copybara/sync

Conversation

@inkeep-oss-sync

Copy link
Copy Markdown
Contributor

Add a first-run onboarding card to OK Desktop. A dismissable checklist in the sidebar footer guides genuinely new users through three momentum steps — create a project (pre-checked), create the first file (⌘N), and ask AI (⌘L) — each showing its keyboard shortcut. Steps check off as the user actually performs them; on finishing all three the card celebrates in place with the OK blob mascot before animating away for good. It shows only for a fresh single-project desktop session and never returns once dismissed or completed (device-local). Web and CLI render nothing.

Two docked-terminal fixes. Dropping a file onto the terminal now inserts its shell-escaped absolute path at the prompt (matching VS Code / Cursor / JetBrains), so you can drag a screenshot straight into a running claude session instead of the drop being ignored. And highlighting text and choosing "Ask AI" now sends the selection straight into the open terminal (or opens the Ask AI composer when no terminal is open) — consistent with the fact that there is no composer visible while the terminal is up. Launching a CLI (Create with…, Open in terminal) always opens a new terminal tab rather than reusing the one already running.

miles-kt-inkeep and others added 6 commits July 1, 2026 06:25
* docs: add Migrate section for Notion and Obsidian imports

Add a Migrate section with import guides for Notion (export to Markdown
and open in OpenKnowledge, with cleanup prompts) and Obsidian (open a
vault in place). Move the existing Obsidian guide out of Get Started
into Migrate, wire the section into the docs nav, and redirect the old
/docs/get-started/obsidian URL to its new home.

* docs: address review feedback on Migrate section

- Restore high-value See also links on the Obsidian page (Editor,
  Timeline and recovery, Ignore patterns)
- Fix product name (OpenKnowledge) and the GitHub sync link text and
  description convention on both pages
- Add icon frontmatter to the Notion page
- Remove the incomplete placeholder callout and add a missing table
  row period on the Notion page

GitOrigin-RevId: 48db6e125f88e1ca70db36cbae10c9ba6eae129b
…page (#2318)

* docs(open-knowledge): add "What OpenKnowledge writes to your system" page

Document the full install + runtime write footprint — npm install, ok init,
ok start, ok mcp, and OK Desktop — grouped by trigger and by in-project vs.
outside-project scope, with the opt-outs collected in one place and the
privacy posture (nothing leaves the machine by default). Cross-linked from
the CLI reference and both Quickstart tabs.

Verified against source: the Claude launch.json preview port is 39848 (the
audit's 3000/3101 were both wrong) and embeddings API keys live in
~/.ok/secrets.yml (0600), not global.yml.

Closes PRD-7234.

* docs(open-knowledge): address review — ok start repair sweeps + framing

- Document ok start's three per-boot repair sweeps (MCP configs incl.
  user-level editor configs, .claude/launch.json, skill files), which can
  write outside the project — previously only attributed to OK Desktop.
  Verified against packages/cli/src/commands/start.ts + repair-mcp-configs.ts.
- Reframe the Desktop "keeping MCP entries current" note as shared with
  ok start, and widen the OK_RECLAIM_DISABLE opt-out row accordingly.
- Clarify the desktop auto-update check is automatic (not opt-in/on-demand)
  in the intro callout and the egress section.
- Broaden the CLI cross-link text to cover install + runtime + desktop.
- Drop the temporal "today" from the page description.
- Add the transparency cross-link to the Obsidian onboarding page.

---------

GitOrigin-RevId: 03438df81d9ba8564707bc40884130f84cf9366c
* feat(open-knowledge): add brand assets download page + logo menu

Adds a /brand page and a right-click menu on the site logo so people can
grab the OpenKnowledge logo and icon (SVG + PNG) to share about us.

- /brand: logo (light + dark bg) and icon tiles with per-asset SVG/PNG
  downloads plus a "Download all" zip, footer + sitemap links.
- Right-click the site logo: copy SVG / download SVG / download PNG, and
  a link to the full kit.
- Assets generated from the logo components (bun run generate:brand-assets):
  flat SVGs keeping the mark's drop-shadow, high-res transparent PNGs, zip.
- MarketingButton: an explicit `download` now forces a raw <a download>
  (next/link dropped it), and `tertiary` honors size="sm".

* fix(open-knowledge): throw on non-ok fetch when copying brand SVG

fetch only rejects on network failure, so a missing/renamed asset would
copy the server's error page to the clipboard. Guard on res.ok so the
caller's catch falls back to the download links.

* fix(open-knowledge): distinct a11y names for brand downloads + use BRAND_ROUTE

- Add sr-only asset name inside each SVG/PNG download link so screen-reader
  users can tell the six links apart (WCAG 2.4.4).
- Use the BRAND_ROUTE constant in sitemap.ts and the page canonical instead
  of a hardcoded /brand.

* fix(open-knowledge): announce brand-logo copy to screen readers

- Add an always-mounted aria-live status region so the 'Copied' confirmation
  in the logo context menu is announced (a text swap on the focused menu item
  isn't reliably announced on its own).
- Correct copy-svg.ts doc comment: the logo menu is now the only consumer.

---------

GitOrigin-RevId: 45d0ebd6fccb96ab1dc35f7b51faa190eca4de25
* docs(open-knowledge): add OpenClaw integration guide

Add docs/content/integrations/openclaw.mdx documenting how to connect
OpenClaw agents to an OpenKnowledge knowledge base over MCP: manual
registration (JSON block / `openclaw mcp add`), `mcp reload` + `doctor
--probe` verification, `ok init`, a copy-pasteable end-to-end smoke test,
route-only vs full preview URLs, agent working rules, and a troubleshooting
table. Add the page to the Integrations nav.

The page auto-flows into /llms.txt, /llms-full.txt, and its per-page .md
route, so the same content serves both human readers and agents.

* docs(open-knowledge): address review feedback on OpenClaw guide

OpenClaw page:
- Cross-reference the installed skill instead of restating its rules
- Consolidate the two verification sections into one (server-side probe +
  agent-side prompt), with their relationship explained
- Condense the Preview section to a cross-reference (dedupes the MCP reference)
- Fold the mental-model framing into the intro; drop the standalone section
- Match the sibling description pattern ("Use OpenKnowledge with X")
- Reframe the unverified `openclaw mcp add` command as a pointer to `--help`;
  the JSON config is the verified path
- Use clearer placeholder paths: /path/to/your/knowledge-base and
  /Users/<your-username>/.ok/bin/ok (avoids literal-paste of /Users/you/...)

reference/mcp.mdx:
- Fix stale "17 tools" -> "19 tools" and add the missing `skills` and `install`
  rows so the count and table agree with the registry and SKILL.md

Move the "Using with Obsidian" guide from Get Started to Integrations so it's
out of the first-run flow; update both section nav files.

* docs(open-knowledge): redirect the moved Obsidian docs URL

Moving "Using with Obsidian" from Get Started to Integrations orphaned
/docs/get-started/obsidian (live since #2249). Add a redirect to its new
home, matching the existing deleted-page pattern in next.config.ts — both
slash variants, since skipTrailingSlashRedirect is on.

---------

GitOrigin-RevId: a65891352c84066ab9506308f1c12faa81713769
* [US-001] add onboarding card persistence store

Module-level singleton backed by localStorage and bound to React via
useSyncExternalStore. Tracks onboarding step completion, collapse,
dismissal, and completion so card state survives remounts and reloads;
the dismissed and completed flags persist device-local so a dismissed or
finished card never returns.

Mirrors the update-notices-store module-store shape and adds localStorage
write-through (that store is in-memory only). Installed at main.tsx
module-init; SSR-safe when localStorage is unreachable.

* [US-002] add new-user visibility hook (useOnboardingCardVisible)

Host-gated, fail-safe fresh-project predicate (listRecent filtered to other
switchable projects + /api/documents entry count 0) with an activation latch so
the card persists through step completion. evaluateFreshProject resolves false
on any bridge/fetch failure (suppress rather than ambush). 7 unit + 6 DOM tests.

* [US-003] wire onboarding step-completion signals (file + Ask AI)

File step: useOnboardingFileCompletion watches the documents-changed bus and
marks the step once entry count >= 1 (with a mount-time check). Ask-AI step:
recordOnboardingAskedAi fires from BottomComposer's two successful-dispatch
paths, gated on an active onboarding session. Shared document-count fetch
extracted to onboarding-document-count.ts. 6 unit + 3 DOM tests added.

* [US-004] add OnboardingCard expanded UI (steps, shortcuts, click-to-act, dismiss)

Three-step checklist: step 1 pre-checked (endowed progress), steps 2/3 show
registry shortcuts (formatShortcutLabel) and are clickable to fire the create-file
/ open-Ask-AI actions. Completion indicator is a presentational aria-hidden
Checkbox; the actionable affordance is a real Button with a full accessible name.
Dismiss routes through store.dismiss(). Injectable store seam for tests; 5 DOM tests.

* [US-005] add collapse-to-pill and auto-closing success state

Collapse toggle persists via store.setCollapsed; collapsed renders a compact
'Setup · N of 3' pill that expands on click. Completing all three steps renders
a polite live-region success message that auto-closes after ~4s and marks the
card completed so it never returns (timer cleared on unmount; motion-safe fade).
autoCloseMs is a test seam. +2 DOM tests.

* [US-006] mount gated onboarding card in editor pane + extract i18n catalogs

OnboardingCardMount evaluates the visibility predicate and renders the card
floating bottom-left of the editor-column panel (right of the sidebar, hidden
below a narrow breakpoint); renders nothing on web/CLI. Regenerated en + pseudo
Lingui catalogs for the new card strings. +2 DOM tests for the gated mount.

* test(onboarding): assert Ask-AI step records only on successful dispatch

Closes the R5 negative-half gap from the spec-fidelity check: pins that
recordOnboardingAskedAi fires after BottomComposer's submit guards, so an
aborted submit (null compose input) leaves the Ask-AI step unchecked.

* fix(onboarding): address local review findings

Major:
- Ask-AI step now records only on a CONFIRMED-successful dispatch (gate inside
  .then on outcome.ok); a failed handoff ({ok:false}) no longer checks it off —
  fixes silent onboarding-completion for the exact new-user population. + tests
  for success / abort / {ok:false}.
- Add changeset for the user-facing feature.

Minor:
- recordOnboardingFileStep gains the initialized guard its comment claimed (symmetry).
- console.warn breadcrumbs on the fail-safe catches (store write, listRecent/fetch, file step).
- Replace fixed-duration setTimeout waits in negative DOM tests with deterministic waitFor.
- Persistent always-rendered sr-only polite live region for the success announcement.
- Add onboarding-document-count.test.ts (kind/hidden filtering + fetch/parse).

* fix(onboarding): glyph shortcuts, non-interactive step rows, full-opacity checkbox

Per design feedback:
- Render shortcuts via the reusable Kbd component + formatShortcut (⌘ N / ⌘ L
  glyphs) instead of the verbose 'Command N' label.
- Make step rows informational only (drop click-to-act) — the clickable Button
  rows read as confusing; steps check off as the user performs ⌘N / ⌘L.
- Render the decorative completion checkbox at full opacity (override the
  disabled dim) since it's a status glyph, not a form field.
- a11y preserved: rows convey completion via strikethrough + an sr-only marker.

* feat(onboarding): move card to sidebar footer + success toast; drop collapse/pill

Per design feedback (simplification):
- Mount the card in the sidebar footer (FileSidebar) instead of a floating overlay
  in the editor pane — anchored, full-width app chrome; removes the absolute
  positioning, z-index, breakpoint, and footer/composer-overlap concerns.
- Completion now fires a Sonner success toast ("You're all set up! 🎉") instead of
  an in-card success state — deletes the success branch, auto-close timer, and
  persistent live region (Sonner handles announcement). Toast + markCompleted are
  deferred off the mount commit so the unmount can't race-drop the toast; the card
  lingers ~1.5s at 3/3, then auto-closes.
- Drop the collapse-to-pill (and the store's collapsed/setCollapsed) — redundant
  in the footer; simpler component + store.

* feat(onboarding): re-add collapse — card folds to its header row

In the sidebar footer the card folds to a single 'Finish setting up · N/3' header
row with an expand chevron (persisted via store.collapsed); expanding restores the
full checklist. Restores collapsed/setCollapsed on the store. Dismiss stays the
permanent opt-out; collapse is the lightweight reclaim-space escape.

* feat(onboarding): celebrate completion in-card with the blob mascot (drop toast)

The success toast popped bottom-right, disconnected from the bottom-left card.
Replace it with an in-card celebration: on completing all three steps the card
transforms into the OkBlob mascot doing a happy-eyes firework burst beside an
'all set up' message (celebrateSignal — on-brand, no new dependency, reduced-
motion-aware), lingers ~2.8s, then auto-closes. Removes the Sonner dependency
from the card and the toast-render timing workarounds. Completion is announced
to assistive tech via an sr-only live region.

* feat(onboarding): make the completion celebration last longer + keep it lively

The single 2.8s burst was easy to miss. Extend the dwell to ~5.2s and re-fire the
blob's firework every 1.5s so it keeps popping over the whole celebration instead
of bursting once and going static, then auto-close.

* feat(onboarding): remove collapse — card is just checklist + Dismiss

Collapse didn't save meaningful space in the footer, and onboarding isn't
essential enough to warrant the extra control — Dismiss already covers opting
out. Drops the collapse toggle, the folded header state, and the store's
collapsed/setCollapsed.

* feat(onboarding): animate the celebration in and out (no abrupt close)

The celebration appeared and vanished abruptly. Add CSS-first transitions
following the repo's tw-animate-css convention (no Motion dependency):
- enter: animate-in fade-in-0 zoom-in-95 (300ms) as the checklist gives way
- exit: an 'exiting' state plays animate-out fade-out-0 zoom-out-95 (200ms) and
  holds the unmount until it finishes, so markCompleted no longer cuts it off
- reduced motion: animate-none / duration-0 (instant, no jank)

* fix(onboarding): hold the celebration exit's faded state (fix close flicker)

tw-animate-css's 'exit' keyframe only defines a 'to' state and defaults
fill-mode:none, so at the end of the fade-out the card snapped back to opacity 1
for the ~20ms before unmount — a flicker. Add fill-mode-forwards so it holds the
faded-out end state until markCompleted unmounts it. Verified: opacity ramps to
0.00 and stays there through unmount (no 1.0 snap-back).

* docs(changeset): update onboarding-card changeset to final design

* fix(onboarding): header copy "Get setup" -> "Get set up"

Reviewer (pullfrog) flagged the header as non-idiomatic: "set up" is the
verb phrase, "setup" the noun. The imperative reads "Get set up". Regenerate
Lingui catalogs for the changed string.

* fix(onboarding): symmetric read-path logging + Zod cause on schema mismatch

Two reviewer (claude[bot]) diagnostics:
- readPersistedState now console.warns on catch, matching writePersistedState
  — distinguishes a silent privacy/parse degradation from a correct
  not-yet-activated state when debugging "card never appears".
- fetchDocumentEntryCount attaches the Zod error as `cause` so a /api/documents
  shape change pinpoints the failing field in caller logs.

* fix(onboarding): pre-register completion live region + aria-labelledby heading

Two reviewer (claude[bot]) a11y findings on the card:
- The completion live region was mounted already-populated inside the
  allComplete branch; VoiceOver/Safari miss a region added and filled in one
  render. Mount it unconditionally at the component root, empty until complete,
  so it is pre-registered (WCAG 4.1.3). Reduced-motion users who can't see the
  blob rely on this announcement.
- Replace the checklist section's aria-label ("Getting started") with
  aria-labelledby pointing at the visible <h2> ("Get set up") so landmark nav
  and heading nav announce one string (WCAG 2.4.6); drops a redundant i18n key.

* test(onboarding): cover terminal-launch-throws guard + already-complete early-return

Closes two reviewer (claude[bot]) Consider-level test-gap recommendations:
- BottomComposer: a throwing launchInTerminal must keep the draft, toast, and
  record no Ask-AI step. Asserts the guard's user-observable outcomes (draft
  preserved, error toast, step not recorded) — mutation-verified RED if the
  catch's early-return is removed.
- useOnboardingFileCompletion: an already-complete file step performs no
  /api/documents fetch (defense-in-depth over recordOnboardingFileStep's own
  steps.file guard) and ignores later files events.

* test(onboarding): isolate the steps.file guard arm in the already-complete no-op test

Reviewer (claude[bot]) caught that the "no-op when already complete" test never
called activate(), so `initialized` stayed false and the guard's `!initialized`
arm alone satisfied it — dropping `steps.file ||` from the guard would still
pass. activate() first so only the steps.file arm is exercised. Mutation-verified:
removing `steps.file ||` now fails this test.

* docs(onboarding): drop stale "collapse" from two comments

The collapse affordance was removed from the card design; OnboardingCardState
has no `collapsed` field. Remove the leftover word from the store JSDoc and the
main.tsx hydration comment so they match the shipped state shape.

* test(onboarding): cover non-files channel filter + live-region silence

Two reviewer (claude[bot]) Consider-level coverage suggestions:
- useOnboardingFileCompletion: a non-files documents-changed channel (graph,
  backlinks) triggers no extra /api/documents fetch. Mutation-verified RED if
  the channels.includes('files') guard is removed.
- OnboardingCard: the pre-registered live region stays empty (silent) during the
  checklist state — the other half of the WCAG 4.1.3 pre-registration invariant.
  Mutation-verified RED if the allComplete ternary is broken.

---------

GitOrigin-RevId: b3e89b56ade28f0a4acef3b59f417f0c1915f67d
* fix(open-knowledge): terminal file-drop path insertion and Ask-AI terminal reuse

Drop a file onto the docked terminal to insert its shell-escaped absolute
path at the prompt (VS Code / Cursor / JetBrains parity) instead of the drop
being ignored; the CLI reads the file from disk. Resolves the dropped File to
its path via a new bridge getPathForFile (Electron webUtils.getPathForFile),
since Electron removed File.path.

Ask AI on a selection now routes the launch into the terminal that is already
open (writing into the active session's live PTY) rather than spawning a fresh
tab per intent. The + control and Terminal > New Terminal menu still open new
tabs, so multi-session is preserved.

PRD-7238, PRD-7239

* fix(open-knowledge): actually route Ask-AI into the open terminal's live PTY

The prior commit's message claimed the Ask-AI reuse fix but only shipped the
file-drop change; the terminal-reuse logic was never implemented. The spec's
plan (re-hand the launch to the active session, zero panel changes) was written
before the movable-dock refactor, which made launches bake into the PTY spawn.
Mutating the active session's launch now re-runs the mount effect and respawns
(kills) the running shell, defeating the point.

Reuse instead by writing into the live PTY: each panel reports its resolved
ptyId up via a new onPtyId callback (TerminalPanel, TerminalGate), and the host
keeps a per-session ptyId map. On a new launch nonce with an active live
session, the host writes buildCliLaunchArgString(cli, prompt) into that PTY via
bridge.terminal.input rather than opening a tab; it falls back to a new tab when
no session has a live PTY yet. Adopted reload-survivors report their ptyId too,
so reuse covers them (fixes the review's silent-drop concern).

Known Phase-1 limit unchanged: writing into a shell prompt runs the command; a
CLI already running interactively receives it as literal text.

PRD-7239

* test(open-knowledge): pin adopted-session reuse and null drop-path edge cases

Address the automated PR review's two test-hardening suggestions.

Adopted-session Ask-AI reuse: extend the reload-survival dock test so a
rehydrated survivor reports its adopted ptyId up (mirroring the real
attachSession callback), then a launch writes the command straight into that
adopted PTY with no new tab and no fresh create. Pins the reload-survivor reuse
claim that was previously only asserted in a source comment.

File-drop null paths: add TerminalPanel drop tests for getPathForFile returning
empty/null. An all-null drop (clipboard blob, no disk backing) writes nothing;
a mixed drop writes only the resolvable path, never a literal null or empty
shell argument.

* fix(open-knowledge): Ask-AI bubble button sends selection into the open terminal

The WYSIWYG bubble-menu Ask AI button now sends the selected passage
straight into the active shell (e.g. a running claude TUI) via a new
active-terminal-input channel; the host owns the PTY state and falls back
to the bottom Ask-AI composer when no terminal is open or the selection is
empty. Mirrors the terminal-launch-events idiom but the payload is verbatim
text, no CLI discriminant.

PRD-7239

* fix(open-knowledge): CLI launches always open a new terminal tab

A 'Create with CLI' / 'Open in terminal' / bottom-composer launch was reusing
an already-open terminal — writing the launch command into the live PTY of a
shell the user already had running (e.g. dropping a codex launch into a running
claude). That was the wrong reuse surface: 'create' means a fresh terminal.

Reuse of an open terminal now belongs solely to the selection bubble
(requestActiveTerminalInput), which types highlighted text into the live shell.
The launch-nonce path always opens its own tab; the panel still bakes the CLI
launch into that fresh PTY spawn.

PRD-7239

* fix(open-knowledge): defer Ask-AI bubble dispatch a frame; align reuse tests+comments

Ask-AI bubble click deferred to rAF so ProseMirror's mouseup focus can't
steal the caret back before composer/terminal focus lands. Reload-survivor
reuse test now exercises requestActiveTerminalInput (launches always open a
new tab), and the stale ptyIdBySessionRef comments now name the selection-
input reuse path.

* fix(open-knowledge): capture-phase terminal drop listeners; test selection reuse

Terminal external-drop dragover/drop listeners now bind in the capture phase
(matching FileTree) so xterm's canvas child can't swallow the event before the
container sees it — the behavior the existing comment already claimed. Adds the
counterpart reuse test: requestActiveTerminalInput writes the raw selection
straight into the live PTY (no wrapping, no new tab) while launches open their
own tab.

* fix(open-knowledge): filter control chars from dropped paths; pin gate PTY forwards

Dropped filenames are untrusted input written into the live PTY. A path with an
embedded newline/CR is acted on by the tty line discipline before shell quoting,
so it could auto-submit a partial command (injection via a dropped file).
shellSingleQuote keeps the bytes shell-inert but can't stop the tty acting first
— so the drop path now filters any path carrying an ASCII control char (codepoint
scan, not a regex, so no lint-disable to strip on the public mirror).

Also extends the TerminalGate forwarding test to pin onPtyId + adoptPtyId: the
gate is the only host->panel crossing for those wires and every Dock/reload test
stubs the gate, so a dropped forward would silently break reuse + reload survival
while passing all of them.

---------

GitOrigin-RevId: f0eeecbc6ad4493b2d5715532e905f66eb155e06

@inkeep-internal-ci inkeep-internal-ci Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval from agents-private public-mirror-sync (run: https://github.com/inkeep/agents-private/actions/runs/28497989378). Source of truth is the monorepo; direct edits on inkeep/open-knowledge are overwritten on next sync.

@inkeep-oss-sync
inkeep-oss-sync Bot merged commit b780bf6 into main Jul 1, 2026
1 check passed
@CLAassistant

CLAassistant commented Jul 1, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@inkeep-oss-sync
inkeep-oss-sync Bot deleted the copybara/sync branch July 1, 2026 06:26
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.

5 participants