Skip to content

refactor(editor): extract theme + highlight from MarkdownEditor#283

Merged
github-actions[bot] merged 1 commit into
refactor/split-note-repositoryfrom
refactor/split-markdown-editor
Jun 9, 2026
Merged

refactor(editor): extract theme + highlight from MarkdownEditor#283
github-actions[bot] merged 1 commit into
refactor/split-note-repositoryfrom
refactor/split-markdown-editor

Conversation

@tomymaritano

Copy link
Copy Markdown
Collaborator

Summary

Phase 1 of the MarkdownEditor split. Pure theme + markdown HighlightStyle move to their own file so further extractions (extensions array, keymap bindings) can ride on top without merging against a churn-prone component shell.

Metric Before After
`MarkdownEditor.tsx` 737 lines 612 lines (-17%)
`editorTheme.ts` 139 lines (new)

Extracted

Symbol Kind
`SCROLL_PAST_END_PADDING` constant
`createEditorTheme(fontSize, fontFamily, lineHeight)` factory returning `EditorView.theme({...})`
`markdownHighlighting` `HighlightStyle.define([...])` with all tag styles for markdown

Imports cleaned up

  • Drop `HighlightStyle` from `@codemirror/language` import (no longer referenced in this file)
  • Drop `tags` from `@lezer/highlight` (moved into editorTheme.ts)

What this PR DELIBERATELY does NOT do

  • Extract the extensions array (~96 lines of `createExtensions`). It closes over user settings (`lineNumbersCompartment` etc.) and mixes context-coupled values like `wikilinkAutocomplete` from a hook. Safely pulling that out requires either passing the closure context via a builder, or moving the whole `useMemo` into its own hook. Better done under Playwright coverage (PR-E feat(e2e): scaffold Playwright Electron suite + CI job (PR-E) #277) so renderer regressions surface.
  • Extract the keymap. Same reason — bindings reference view-imperatives + the command-registry which are constructed inside the React tree.

Test plan

  • `pnpm -r typecheck` — green (renderer + e2e tsconfigs)
  • `pnpm test` — 17/17 packages
  • Manual: open the editor, type in a markdown note with headings, emphasis, lists, code blocks. The look must be identical (theme is byte-for-byte the same; just moved).

Stack context

Stacked on note repo split (#282) → PR-F3 wiring (#281) → PR-Knip-2 (#280) → PR-Knip-1 (#279) → PR-G (#278) → PR-E (#277) → ... down to PR-B (#265). 19 PRs deep.

🤖 Generated with Claude Code

Phase 1 of the MarkdownEditor split. Pure CSS-variable-based theme and
markdown HighlightStyle move to their own file so further extractions
(extensions array, keymap bindings) can ride on top without merging
against a churn-prone component shell.

MarkdownEditor.tsx: 737 -> 612 lines (-17%).
editorTheme.ts: new (139 lines).

Extracted:
- SCROLL_PAST_END_PADDING constant
- createEditorTheme(fontSize, fontFamily, lineHeight) — EditorView.theme(...)
- markdownHighlighting — HighlightStyle.define([...]) for markdown tags

Imports cleaned up:
- Drop the explicit HighlightStyle import from @codemirror/language (still
  consumed transitively, but not referenced in this file anymore)
- Drop the `tags` import from @lezer/highlight (moved to editorTheme.ts)

What this PR DELIBERATELY does NOT do:
- Extract the extensions array (lines 348-444 today). The array closes
  over user settings (lineNumbersCompartment etc.) and mixes
  context-coupled values like `wikilinkAutocomplete` from a hook; safely
  pulling that out requires either passing the closure context in via a
  builder, or moving the whole `useMemo` into a hook of its own. Better
  done under Playwright coverage (PR-E #277) so renderer regressions
  surface.
- Extract the keymap. Same reason — the bindings reference
  view-imperatives + the command-registry which are constructed inside
  the React tree.

Validates:
- pnpm -r typecheck — green (renderer + e2e tsconfigs)
- pnpm test — 17/17 packages

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
readide Building Building Preview, Comment Jun 9, 2026 1:39am

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • main
  • develop

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6d20fd90-4cd5-47bc-a382-812e909bacf1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/split-markdown-editor

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 and usage tips.

@github-actions github-actions Bot merged commit 4645a4b into refactor/split-note-repository Jun 9, 2026
5 of 6 checks passed
@github-actions github-actions Bot added the size/L label Jun 9, 2026
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
## Summary

Replaces the all-zeros placeholder from #281 with a real Ed25519 public
key. The matching private key was generated in this session and is held
outside the repo — see notes below.

## Public key (this PR)

\`\`\`
d049019b2ff05ccfd3802e0619d5897e21431a6f946af724c13ed7ecca7ec01f
\`\`\`

Public by design — the client needs it to verify. Safe to commit.

## Private key (NOT in this PR)

Lives only on the licensing server. Tomy received it in the dev session
that produced this PR and is responsible for storing it as the server's
\`LICENSE_SIGNING_PRIVATE_KEY\` env var (Vercel env / Doppler /
1Password / wherever your secrets live).

**This keypair was generated in a dev chat session and is considered
"dev/staging-grade".** For production, rotate before the first signed
envelope ships: generate a new pair on a trusted machine, push a new
desktop release with the new public key here, then switch the server.
The rotation procedure is documented in the constant's comment.

## Behavior change

| State | Before (placeholder) | After (real key) |
|---|---|---|
| Cache without envelope | Warning logged, accepted (lenient) | Warning
logged, accepted (lenient) |
| Cache with envelope signed by matching key | Cannot occur | Silently
accepted |
| Cache with envelope signed by wrong key | Refused → refetch | Refused
→ refetch |
| Cache with tampered envelope | Refused → refetch | Refused → refetch |

The lenient branch (no envelope) is unchanged — existing users are
unaffected. The strict branch (envelope present) now actually works:
real envelopes verify, fakes get rejected.

## Test plan

- [x] \`pnpm --filter @readied/desktop typecheck:main\` — green
- [ ] After merge: server team uses \`signSubscriptionPayload(payload,
LICENSE_SIGNING_PRIVATE_KEY)\` from \`@readied/licensing\`. Round-trip
test:
\`\`\`bash
cd packages/licensing
node -e "
import('./src/validator.js').then(async ({ signSubscriptionPayload,
verifySubscriptionSignature }) => {
  const PRIV = process.env.LICENSE_SIGNING_PRIVATE_KEY;
  const env = await signSubscriptionPayload({
    payloadVersion: 1,
subscription: { subscriptionId:'sub_test', customerId:'cus_test',
email:'x@x.com', plan:'monthly', status:'active', currentPeriodStart:
new Date().toISOString(), currentPeriodEnd: new
Date(Date.now()+30*86400e3).toISOString(), cancelAtPeriodEnd:false },
    issuedAt: new Date().toISOString(),
  }, PRIV);
const r = await verifySubscriptionSignature(env, { publicKey:
'd049019b2ff05ccfd3802e0619d5897e21431a6f946af724c13ed7ecca7ec01f' });
  console.log(r.valid ? '✅' : '❌ ' + r.error);
});
"
\`\`\`

## Stack context

**PR-Set-Key** at the tip of the stack. Sits on top of editor split
(#283) → note repo split (#282) → ... down to PR-B (#265). **20 PRs
deep.**

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
github-actions Bot pushed a commit that referenced this pull request Jun 9, 2026
## Summary

Promotes `develop` to `main` for **v0.15.0**. Originally opened
2026-04-24; now refreshed with the 19-PR tech-debt audit shipped via
#285 plus the accumulated dependabot bumps reconciled.

`semantic-release` will pick the version bump. Expected: **minor
(v0.14.x → v0.15.0)** because of multiple \`feat:\` commits.

## What ships (audit highlights, from #285)

### Runtime fixes (user-facing)
- **Editor no longer crashes on table-containing notes** (#266) —
\`Decoration.replace\` over multi-line ranges moved from a ViewPlugin to
a StateField, plus an EditorView.exceptionSink so any future plugin
error no longer tears down the EditorView.
- **AI keys survive sleep/wake** (#275) — \`aiKeyStorage\` stopped
silently deleting the encrypted store when the keychain was temporarily
locked after macOS sleep.
- **Backup restore is now safe** (#271) — restored DBs go through
\`PRAGMA integrity_check\` before being swapped in; corrupt backups roll
back to the safety copy.
- **MCP server runs without electron-builder rebuilds** (#264#270) —
migrated from native \`better-sqlite3\` to built-in \`node:sqlite\`
(Node 22.5+), updated to the new \`registerTool\` MCP SDK API.

### Security
- **Typed IPC boundary** (#272 + #273 + #274) — 130+ IPC channels now
validated with Zod tuples at the main↔renderer boundary. Garbage in
fails fast with \`IpcValidationError\` instead of corrupting downstream
code.
- **Ed25519 license verification scaffolding** (#276 + #281 + #284) —
\`signSubscriptionPayload\` / \`verifySubscriptionSignature\` helpers
ship in \`@readied/licensing\`, wired into
\`FileLicenseStorage.readSubscriptionData\` with lenient fallthrough
during migration. Real public key embedded (\`d04901…\`). Server-side
\`LICENSE_SIGNING_PRIVATE_KEY\` already set in Cloudflare staging +
production.

### Developer experience
- **Husky → Lefthook** (#267) plus lint-staged that now runs ESLint, not
just Prettier.
- **\`knip\` added** (#267) + 12 unused files deleted (#279) + 6 unused
deps dropped (#280).
- **Playwright Electron E2E scaffold** (#277) with smoke + notes-IPC
specs and a Linux+xvfb CI job (\`continue-on-error: true\` while it
stabilises).
- **Vitest coverage baseline** (#269) — 12 packages share a coverage
config; smoke tests added for \`@readied/commands\`.

### Refactor (no behavior change)
- **Zustand selectors migration** (#268) — 3 components stopped
destructuring entire stores.
- **God-file extractions**:
- \`main/index.ts\` 1065 → 950 lines (#278) — \`FileLicenseStorage\`,
\`windowState\` extracted to services
- \`SQLiteNoteRepository.ts\` 1121 → 1038 lines (#282) — pure helpers
extracted to \`noteMapping.ts\`
- \`MarkdownEditor.tsx\` 737 → 612 lines (#283) — theme +
markdownHighlighting extracted to \`editorTheme.ts\`

## Deploys triggered

| Workflow | Trigger | What happens |
|---|---|---|
| \`deploy-api.yml\` | Auto on \`push\` to main affecting
\`packages/api/**\` | Tests + deploys \`@readied/api\` to Cloudflare
Workers (\`readied-api-production\`). This stack only touched
\`wrangler.toml\` + \`.dev.vars\` docs, no production code change. |
| \`release.yml\` | Manual \`workflow_dispatch\` post-merge |
\`semantic-release\` analyses conventional commits, bumps version,
creates GitHub Release draft + tag |
| \`build.yml\` | Auto on tag push from release.yml | mac / windows /
linux parallel builds, artefacts attached to the GitHub Release |

## Pre-merge verification (local, this branch)

- ✅ \`pnpm -r typecheck\` — green across 18 workspace projects
- ✅ \`pnpm test\` — 17/17 packages
- ✅ Merge resolved: take develop versions for 19 conflicted
package.jsons (develop has equal or newer deps than main's dependabot
bumps)

## Post-merge action items (operator)

1. **Deploy API to staging first** (smoke test):
   \`\`\`
   gh workflow run deploy-api.yml -f environment=staging
   \`\`\`
2. Confirm staging API responds correctly (subscription endpoint with
new \`LICENSE_SIGNING_PRIVATE_KEY\` secret already set in CF).
3. Merge this PR → auto-deploys API to production.
4. Trigger Release workflow: GitHub → Actions → Release → "Run workflow"
→ main.
5. Watch Build workflow for mac/win/linux completion.
6. Confirm the release un-drafts itself.

## Known risks / follow-ups

- **Pre-existing Vercel preview failure for \`apps/web\`** — marketing
site, scheduled to be extracted to its own repo (P3 in the roadmap).
- **\`SUBSCRIPTION_PUBLIC_KEY\` is dev-grade** — generated in a Claude
session. Before the licensing server emits envelopes for real paid
users, rotate the keypair from a trusted machine and ship a follow-up
release.
- **Branch protection should require CodeRabbit completion before
automerge** — added to the roadmap as a process item; this very PR was
BLOCKED correctly because of that policy gap being closed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant