Skip to content

Add whiteboard and chalkboard workspace themes - #23

Merged
donovan-yohan merged 3 commits into
mainfrom
feat/visual-first-whiteboard-chalkboard
Aug 8, 2026
Merged

donovan-yohan merged 3 commits into
mainfrom
feat/visual-first-whiteboard-chalkboard

Conversation

@donovan-yohan

@donovan-yohan donovan-yohan commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner

Outcome

Turns the workspace into a visual-first whiteboard/chalkboard surface while preserving fully customizable Mermaid item colors.

What changed

  • adds browser-local System / Light / Dark theme control with no-flash initialization
  • replaces generic navy/purple chrome with semantic monochrome tokens and intentional interaction accents
  • extracts reusable tab strip, source/activity flyouts, and simple footer from the workspace coordinator
  • preserves authored Mermaid style / classDef colors—including custom circle/cross markers and !important cascade—across themes
  • keeps flyouts overlaid and camera-stable; Fit and floating controls use the measured unobscured viewport
  • hardens 320px control visibility/clickability and focus return
  • adds a production-owned Playwright workspace UX gate and CI job
  • hardens the E2E harness with frozen installs, bounded requests, safe teardown, and one stale-revision retry
  • records durable ownership/invariant context without adding a deeper nested AGENTS file

Architecture decision

Extend the existing Yjs/Mermaid/React Flow architecture. Theme, open-flyout state, selection, camera, and drafts remain browser-local; Mermaid source and source-owned item styles remain canonical/shared. One ThemeProvider resolves visual theme and feeds derived render inputs without creating another state service.

Verification

  • pnpm install --frozen-lockfile
  • pnpm typecheck
  • pnpm lint
  • pnpm test — 24 files / 94 tests
  • pnpm build
  • pnpm test:e2e-workspace-ux — 14 pass / 0 skip
  • pnpm test:e2e-sequence
  • pnpm test:e2e-collaboration
  • exact 1487×1058 light/dark reference comparison: no remaining P0/P1 or changed-visual P2
  • 320px Zoom− / Zoom+ / Fit / Add-node hit-tested and clicked
  • unselected graph ink contrast: light 6.08:1; dark 5.65:1
  • all 13 actionable review threads addressed and resolved
  • no scoped test services left listening

Follow-up discovered

#22 tracks a pre-existing #21 React Flow controlled-node warning exposed by real drag gates. Its safe repair needs a dedicated drag/Yjs ownership slice; this PR does not alter that ownership model.

Closes #14

@vercel

vercel Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
arielcharts Ready Ready Preview Aug 8, 2026 9:55am

@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a browser-local light, dark, and system theme system. It refactors workspace controls into components, preserves flyout and canvas state, renders Mermaid styles in React Flow, improves responsive toolbar placement, and adds a comprehensive production Playwright validation gate.

Changes

Workspace UX and theme system

Layer / File(s) Summary
Theme foundation
apps/web/public/theme-init.js, apps/web/src/lib/theme.ts, apps/web/src/components/theme-provider.tsx, apps/web/src/components/theme-control.tsx, apps/web/src/app/globals.css, apps/web/src/app/layout.tsx
Adds persisted theme preferences, system-theme resolution, pre-hydration initialization, semantic light/dark tokens, Mermaid theme variables, contrast utilities, and root provider wiring.
Canvas rendering and geometry
apps/web/src/components/diagram-canvas.tsx, apps/web/src/lib/mermaid-presentation.ts, apps/web/src/lib/canvas-viewport.ts, apps/web/src/lib/toolbar-safe-area.ts, apps/web/src/lib/renderer-camera-policy.ts, apps/web/src/lib/canvas-keyboard-ownership.ts
Preserves Mermaid-authored colors and styles, applies theme-aware canvas paints, fits around flyout occlusion, positions toolbars within safe bounds, refits on renderer-kind changes, and scopes Escape handling to the canvas.
Workspace component integration
apps/web/src/components/session-workspace.tsx, apps/web/src/components/workspace-tab-strip.tsx, apps/web/src/components/workspace-flyouts.tsx, apps/web/src/components/workspace-footer.tsx
Extracts tabs, flyouts, footer, and theme controls. Adds keyboard navigation, flyout exclusivity, focus restoration, theme-aware editor rendering, and resolved-theme propagation.
Workspace UX end-to-end validation
e2e-workspace-ux-validate.ts, e2e/support/*, package.json
Adds service orchestration, browser and interaction helpers, MCP revision helpers, workspace fixtures, and production checks for themes, focus, layout stability, rendering, contrast, responsive controls, and remote updates.
CI and guidance
.github/workflows/ci.yml, apps/web/AGENTS.md, apps/web/next.config.ts, docs/context-map.md
Adds the CI workspace UX job, documents the workspace invariants and test command, disables development indicators, and updates the architecture context map.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement the linked issue's theme, stable workspace UX, Mermaid color preservation, accessibility, and focused end-to-end coverage objectives.
Out of Scope Changes check ✅ Passed The changes remain focused on workspace themes, workspace UX behavior, supporting utilities, documentation, and the related end-to-end CI gate.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: adding whiteboard and chalkboard workspace themes.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/visual-first-whiteboard-chalkboard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@donovan-yohan
donovan-yohan marked this pull request as ready for review August 8, 2026 09:34

@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: 13

🧹 Nitpick comments (8)
.github/workflows/ci.yml (1)

40-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the frozen lockfile in the workspace job.

A lockfile is committed, and this workflow runs in CI, so pnpm install should enforce lockfile consistency. --frozen-lockfile=false disables that protection and can resolve a dependency graph different from pnpm-lock.yaml. Use pnpm install --frozen-lockfile.

Proposed fix
-      - run: pnpm install --frozen-lockfile=false
+      - run: pnpm install --frozen-lockfile
🤖 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 @.github/workflows/ci.yml at line 40, Update the workspace job’s pnpm install
command to use --frozen-lockfile instead of --frozen-lockfile=false, ensuring CI
enforces the committed pnpm-lock.yaml.
e2e/support/owned-services.ts (1)

138-144: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Preserve the original error as cause.

The catch block builds a new Error from the message text only. The original stack is lost, which makes failures inside run() hard to diagnose. Attach the original error.

♻️ Proposed change
-    throw new Error(`${error instanceof Error ? error.message : String(error)}${diagnostics ? `\n${diagnostics}` : ''}`);
+    throw new Error(`${error instanceof Error ? error.message : String(error)}${diagnostics ? `\n${diagnostics}` : ''}`, { cause: error });
🤖 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 `@e2e/support/owned-services.ts` around lines 138 - 144, Update the catch block
in run() to attach the caught error as the cause when constructing the
diagnostic Error, while preserving the existing combined message and
process-output diagnostics.
e2e/support/browser.ts (1)

42-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use tmpdir() instead of a hardcoded /tmp path.

e2e/support/owned-services.ts already uses tmpdir() from node:os. Use the same approach here so screenshot capture does not depend on a POSIX-only path.

♻️ Proposed change
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
+
 export async function saveScreenshot(page: Page, name: string): Promise<string> {
-  const path = `/tmp/arielcharts-${name}.png`;
+  const path = join(tmpdir(), `arielcharts-${name}.png`);
   await page.screenshot({ path, fullPage: true });
   return path;
 }
🤖 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 `@e2e/support/browser.ts` around lines 42 - 46, Update saveScreenshot to import
and use tmpdir() from node:os when constructing the screenshot path, replacing
the hardcoded /tmp prefix while preserving the existing filename and screenshot
behavior.
e2e/support/workspace.ts (2)

66-83: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

canonicalSource and waitForSource duplicate the line-cleaning logic.

Both functions strip .cm-ySelectionCaret and .cm-widgetBuffer and join lines. The two copies can diverge, and then waitForSource waits for a string that canonicalSource never produces. Define the cleaning function once and inject it into both browser contexts.

♻️ Proposed change
+const READ_CANONICAL_SOURCE = `(() => [...document.querySelectorAll('.cm-line')].map((line) => {
+  const copy = line.cloneNode(true);
+  copy.querySelectorAll('.cm-ySelectionCaret, .cm-widgetBuffer').forEach((node) => node.remove());
+  return copy.textContent ?? '';
+}).join('\\n'))()`;
+
 export async function canonicalSource(page: Page): Promise<string> {
-  return page.locator('.cm-line').evaluateAll((lines) => lines.map((line) => {
-    const copy = line.cloneNode(true) as HTMLElement;
-    copy.querySelectorAll('.cm-ySelectionCaret, .cm-widgetBuffer').forEach((node) => node.remove());
-    return copy.textContent ?? '';
-  }).join('\n'));
+  return page.evaluate<string>(READ_CANONICAL_SOURCE);
 }
 
 export async function waitForSource(page: Page, expected: string): Promise<void> {
-  await page.waitForFunction((source) => {
-    const lines = [...document.querySelectorAll('.cm-line')];
-    return lines.map((line) => {
-      const copy = line.cloneNode(true) as HTMLElement;
-      copy.querySelectorAll('.cm-ySelectionCaret, .cm-widgetBuffer').forEach((node) => node.remove());
-      return copy.textContent ?? '';
-    }).join('\n') === source;
-  }, expected, { timeout: 15_000 });
+  await page.waitForFunction(
+    ([source, read]) => new Function(`return ${read}`)() === source,
+    [expected, READ_CANONICAL_SOURCE] as const,
+    { timeout: 15_000 },
+  );
 }

Alternatively, poll canonicalSource from Node instead of using waitForFunction.

🤖 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 `@e2e/support/workspace.ts` around lines 66 - 83, Extract the shared
line-cleaning and joining logic from canonicalSource and waitForSource into one
reusable function, then inject or reuse that function in both browser contexts.
Ensure both functions remove .cm-ySelectionCaret and .cm-widgetBuffer before
joining line text, so they always produce identical canonical source strings.

59-64: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use ControlOrMeta+A for select-all.

Control+A works on Linux and Windows, but macOS maps Control-A to move the cursor to the line start. Use Playwright’s ControlOrMeta modifier so replaceSource() replaces the editor contents on Apple silicons too.

♻️ Proposed change
   await editor.click();
-  await page.keyboard.press('Control+A');
+  await page.keyboard.press('ControlOrMeta+A');
   await page.keyboard.insertText(source);
🤖 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 `@e2e/support/workspace.ts` around lines 59 - 64, Update replaceSource to use
Playwright’s ControlOrMeta modifier for the select-all keyboard shortcut instead
of Control, while preserving the existing editor interaction and text
replacement flow.
e2e/support/mcp.ts (1)

21-49: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a timeout to the MCP fetch call.

fetch has no signal. If the server accepts the connection and never responds, the gate hangs until the CI job timeout. That hides the failure cause. Use AbortSignal.timeout.

♻️ Proposed change
     const response = await fetch(this.endpoint, {
       method: 'POST',
+      signal: AbortSignal.timeout(15_000),
       headers: {
🤖 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 `@e2e/support/mcp.ts` around lines 21 - 49, Update the MCP request fetch in the
tool-calling method to include an AbortSignal.timeout signal, using an
appropriate finite timeout so unresponsive servers fail promptly while
preserving the existing response and HTTP error handling.
e2e-workspace-ux-validate.ts (2)

238-243: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Replace the fixed 250 ms wait with a condition wait.

page.waitForTimeout(250) makes the camera-fit assertion time dependent. If the static renderer fits later than 250 ms, the assertion at Line 243 fails intermittently in CI. Every other transform check in this file already uses waitForFunction.

♻️ Proposed change
-  await page.waitForTimeout(250);
-  const staticTransform = await layer.getAttribute('style');
-  assert(staticTransform !== zoomed, `Editable-to-static renderer transition did not fit the camera: ${zoomed}`);
+  await page.waitForFunction((previous) => document.querySelector('.diagram-canvas-svg')?.parentElement?.getAttribute('style') !== previous, zoomed, { timeout: 15_000 });
+  const staticTransform = await layer.getAttribute('style');
+  assert(staticTransform !== zoomed, `Editable-to-static renderer transition did not fit the camera: ${zoomed}`);
🤖 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 `@e2e-workspace-ux-validate.ts` around lines 238 - 243, Replace the fixed
page.waitForTimeout(250) in the static renderer transition flow with
page.waitForFunction, waiting until the layer style transform changes from
zoomed before reading staticTransform and asserting the camera fit. Follow the
existing waitForFunction pattern used by other transform checks in the test.

129-137: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse renameActiveDiagram instead of inlining the rename flow.

Lines 129-137 repeat the exact steps of renameActiveDiagram in e2e/support/workspace.ts (Lines 107-115). The only addition is the screenshot. Extend the helper with an optional hook, or call the helper and take the screenshot around it. The helper is already imported at Line 34.

🤖 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 `@e2e-workspace-ux-validate.ts` around lines 129 - 137, The inline rename
sequence should reuse the existing renameActiveDiagram helper instead of
duplicating its steps. Replace the direct button/input/tab assertions in this
test with the imported helper, preserving the issue-14-rename screenshot by
taking it before or after the helper as appropriate; extend the helper only if
needed to support the screenshot hook.
🤖 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 @.github/workflows/ci.yml:
- Line 32: Update the actions/checkout@v4 step to disable persisted credentials
by configuring persist-credentials as false, while leaving the rest of the
workflow unchanged.

In `@apps/web/src/app/globals.css`:
- Around line 56-57: Update the --font-sans and --font-mono declarations to use
the configured lowercase spellings for Nunito, BlinkMacSystemFont, Consolas, and
Menlo, while preserving the existing font-stack order and fallback values.

In `@apps/web/src/components/diagram-canvas.tsx`:
- Around line 2111-2130: The getFlowEdgePresentation function uses fixed marker
IDs for arrow_circle and arrow_cross, preventing presentation.stroke from
coloring their markers. Update these marker types to use presentation-aware
marker definitions or marker references carrying markerColor, while preserving
the existing edge styling and behavior; add coverage verifying authored colors
for both arrow_circle and arrow_cross.
- Around line 1768-1782: Update both bottom toolbar positioning blocks in
apps/web/src/components/diagram-canvas.tsx:1547-1567 and
apps/web/src/components/diagram-canvas.tsx:1768-1782 to derive the bottom offset
from canvasSize.height - (canvasViewport.y + canvasViewport.height), preserving
the existing inset behavior so both toolbars remain within the unobscured canvas
viewport.

In `@apps/web/src/components/theme-provider.tsx`:
- Around line 52-56: Update handleStorageChange to call syncTheme when the
storage event targets THEME_STORAGE_KEY or has a null key from
window.localStorage, thereby handling localStorage.clear(). Add test coverage
for the null-key clear event.

In `@apps/web/src/lib/mermaid-presentation.ts`:
- Around line 72-77: Update the declaration merging logic around
getMatchingRules and parseDeclarations so !important priorities are tracked and
preserved when stylesheet and inline declarations conflict. Ensure important
stylesheet values override normal inline values, while retaining existing
cascade behavior for equal priorities; update the priority-marker handling near
the relevant declaration-processing code and add a regression test covering
conflicting class and inline paint declarations.

In `@e2e-workspace-ux-validate.ts`:
- Around line 400-403: Update expectNoDevelopmentIndicator so page.evaluate
returns a boolean indicating whether the Next.js development-indicator selector
matches, then assert that boolean is false. Keep the existing selectors and
failure message unchanged.

In `@e2e/support/browser.ts`:
- Around line 35-38: Update the close method so context shutdown uses
Promise.allSettled and browser.close() runs in a finally block, ensuring the
browser is always closed even when one or more context.close() calls fail.

In `@e2e/support/interactions.ts`:
- Around line 67-80: Update parseRgb to account for the alpha component in rgba
values before contrastRatio uses the parsed color, including transparent alpha 0
and fractional alpha values; alternatively, explicitly reject non-opaque RGBA
tokens with a clear assertion. Preserve existing RGB and hex parsing behavior.

In `@e2e/support/mcp.ts`:
- Around line 66-77: Update writeLatest to handle the revision race by retrying
the readDiagram/writeDiagram sequence once when writeDiagram fails due to a
revision conflict, while preserving the existing detail and actor metadata;
return the successful Diagram result and propagate non-conflict errors or
failures after the retry.

In `@e2e/support/owned-services.ts`:
- Around line 110-113: Update the comment above baseUrl and mcpUrl to describe
the production-service reason for using localhost, referencing the
ALLOWED_ORIGINS configuration set earlier in the setup. Remove the outdated next
dev and development-origin guard explanation while leaving the URL assignments
unchanged.
- Around line 36-49: Update waitFor in e2e/support/owned-services.ts (lines
36-49) to pass AbortSignal.timeout(5_000) in its fetch options. Update the tool
request in e2e/support/mcp.ts (lines 21-49) to pass AbortSignal.timeout(15_000),
ensuring both bounded operations cannot hang indefinitely.
- Around line 133-135: Replace the npx-based pnpm invocations in the production
build, server start, and web start calls with the repository’s trusted pnpm
command, preserving each existing argument list and environment. Use the
packageManager/Corepack-resolved pnpm version rather than explicitly resolving
pnpm@10.15.0 through npx.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 40: Update the workspace job’s pnpm install command to use
--frozen-lockfile instead of --frozen-lockfile=false, ensuring CI enforces the
committed pnpm-lock.yaml.

In `@e2e-workspace-ux-validate.ts`:
- Around line 238-243: Replace the fixed page.waitForTimeout(250) in the static
renderer transition flow with page.waitForFunction, waiting until the layer
style transform changes from zoomed before reading staticTransform and asserting
the camera fit. Follow the existing waitForFunction pattern used by other
transform checks in the test.
- Around line 129-137: The inline rename sequence should reuse the existing
renameActiveDiagram helper instead of duplicating its steps. Replace the direct
button/input/tab assertions in this test with the imported helper, preserving
the issue-14-rename screenshot by taking it before or after the helper as
appropriate; extend the helper only if needed to support the screenshot hook.

In `@e2e/support/browser.ts`:
- Around line 42-46: Update saveScreenshot to import and use tmpdir() from
node:os when constructing the screenshot path, replacing the hardcoded /tmp
prefix while preserving the existing filename and screenshot behavior.

In `@e2e/support/mcp.ts`:
- Around line 21-49: Update the MCP request fetch in the tool-calling method to
include an AbortSignal.timeout signal, using an appropriate finite timeout so
unresponsive servers fail promptly while preserving the existing response and
HTTP error handling.

In `@e2e/support/owned-services.ts`:
- Around line 138-144: Update the catch block in run() to attach the caught
error as the cause when constructing the diagnostic Error, while preserving the
existing combined message and process-output diagnostics.

In `@e2e/support/workspace.ts`:
- Around line 66-83: Extract the shared line-cleaning and joining logic from
canonicalSource and waitForSource into one reusable function, then inject or
reuse that function in both browser contexts. Ensure both functions remove
.cm-ySelectionCaret and .cm-widgetBuffer before joining line text, so they
always produce identical canonical source strings.
- Around line 59-64: Update replaceSource to use Playwright’s ControlOrMeta
modifier for the select-all keyboard shortcut instead of Control, while
preserving the existing editor interaction and text replacement flow.
🪄 Autofix

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: 3242791a-8479-4f60-ae93-dfe941ff3f8b

📥 Commits

Reviewing files that changed from the base of the PR and between 45bab92 and fc1157b.

⛔ Files ignored due to path filters (2)
  • docs/design/arielcharts-chalkboard-target.png is excluded by !**/*.png
  • docs/design/arielcharts-whiteboard-target.png is excluded by !**/*.png
📒 Files selected for processing (36)
  • .github/workflows/ci.yml
  • apps/web/AGENTS.md
  • apps/web/next.config.ts
  • apps/web/public/theme-init.js
  • apps/web/src/app/globals.css
  • apps/web/src/app/layout.tsx
  • apps/web/src/components/diagram-canvas.tsx
  • apps/web/src/components/session-workspace.tsx
  • apps/web/src/components/theme-control.tsx
  • apps/web/src/components/theme-provider.tsx
  • apps/web/src/components/workspace-flyouts.tsx
  • apps/web/src/components/workspace-footer.tsx
  • apps/web/src/components/workspace-tab-strip.tsx
  • apps/web/src/lib/canvas-keyboard-ownership.test.ts
  • apps/web/src/lib/canvas-keyboard-ownership.ts
  • apps/web/src/lib/canvas-viewport.test.ts
  • apps/web/src/lib/canvas-viewport.ts
  • apps/web/src/lib/mermaid-presentation.test.ts
  • apps/web/src/lib/mermaid-presentation.ts
  • apps/web/src/lib/renderer-camera-policy.test.ts
  • apps/web/src/lib/renderer-camera-policy.ts
  • apps/web/src/lib/theme.test.ts
  • apps/web/src/lib/theme.ts
  • apps/web/src/lib/toolbar-safe-area.test.ts
  • apps/web/src/lib/toolbar-safe-area.ts
  • apps/web/src/lib/workspace-flyout-state.test.ts
  • apps/web/src/lib/workspace-flyout-state.ts
  • docs/context-map.md
  • e2e-workspace-ux-validate.ts
  • e2e/support/assert.ts
  • e2e/support/browser.ts
  • e2e/support/interactions.ts
  • e2e/support/mcp.ts
  • e2e/support/owned-services.ts
  • e2e/support/workspace.ts
  • package.json

Comment thread .github/workflows/ci.yml
Comment thread apps/web/src/app/globals.css Outdated
Comment thread apps/web/src/components/diagram-canvas.tsx
Comment thread apps/web/src/components/diagram-canvas.tsx Outdated
Comment thread apps/web/src/components/theme-provider.tsx
Comment thread e2e/support/interactions.ts
Comment thread e2e/support/mcp.ts
Comment thread e2e/support/owned-services.ts
Comment thread e2e/support/owned-services.ts Outdated
Comment thread e2e/support/owned-services.ts Outdated

This branch was successfully deployed

1 active deployment
Preview — 19d19445 Deployed Aug 8, 2026 by vercel[bot]
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.

Bullet-journal canvas and visual-first direct editing UX

1 participant