# Changelog All notable changes to this extension will be documented in this file. ## [Unreleased] ## [0.3.0] — 2026-08-26 ### Added — The platform: web app, mobile app, Chrome extension, unified updates (#328, #329) Mark It Down grows from two surfaces to four, backed by one cloud service: - **Web app (`apps/web`)** — a [ToGo](https://github.com/togo-framework/togo) (Go) service + TanStack SPA: - Landing page with per-platform install buttons fed live from GitHub Releases. - Auth: register / login / logout, CSRF-guarded sessions, email-OTP password reset (mail-plugin bridge), and **TOTP 2FA** with a challenge-token login flow, QR enrollment, and recovery codes. - **Cloud notes warehouse**: user-scoped notes CRUD (search, categories, tags) with a two-pane editor + live preview, and **public share links** (`/s/{slug}`) rendering sanitized HTML server-side. - **MCP connector**: `POST /mcp` (Streamable HTTP) behind bearer personal access tokens — tool parity with the bundled stdio server, so Claude Desktop/Code and Cursor connect with a copy-paste config from the profile. - **Admin panel**: user management (impersonate, reset, magic links), SMTP settings, and schema-driven resource CRUD. - **Unified update feed**: `GET /api/updates/{platform}` classifies release assets for windows / mac / linux / vscode / chrome with caching. - **Chrome extension (`apps/chrome-extension`)** — MV3: quick capture into the warehouse, a 25-theme markdown viewer for `.md` URLs, options with PAT auth, and a daily update check (Web Store handles auto-update for store installs). - **CI/CD** — the CI matrix now builds the Go API + SPA and the Chrome extension; the release workflow attaches the Chrome zip next to the installers and the `.vsix`. - **Mobile app (`apps/mobile`)** — Flutter (Android/iOS): warehouse client with 2FA login + one-tap developer login, notes list/editor/viewer, public sharing, all 25 themes and the shared design tokens; Android release APK, iOS scaffolded. - **One design system** — the web SPA dropped the UI kit + Tailwind for the desktop's own tokens (`packages/ui-tokens` via `@mid/*` aliases) and a new shared motion layer (`animations.css`: entrances, stagger, skeletons, press & hover feedback, one focus ring, reduced-motion safe), documented in [docs/DESIGN.md](docs/DESIGN.md). Landing page lists all seven install targets including Android (APK) and iOS (soon). - **Web notes workspace** — master/detail layout mirroring desktop/mobile (responsive down to phones), admin on its own activity-bar tab, skeleton loading, and the notes API no longer requires category/tags/body keys. - Docs: [docs/platform.md](docs/platform.md), [docs/chrome-extension.md](docs/chrome-extension.md). ## [0.2.7] — 2026-05-03 ### Fixed — Settings page is no longer a placeholder (#315) Every section now wires to real, persisted settings: - **General** — default mode on launch (View / Split / Edit), open last folder on launch, confirm before closing dirty tabs. - **Appearance** — font family, font size (12–22 slider), preview max-width (600–1100 slider). Theme picker + light/dark mode pills already worked from #233. - **Editor** — word wrap, show line numbers in code blocks, auto-save (Off / on blur / every 5s). - **Notes** — default note type for the "+" button. Existing typed-notes config (filter strip, custom types) already shipped in #297/#302. - **GitHub** — read-only display of the active warehouse, *Change warehouse…* button (re-opens the onboarding flow with `force=true`), *Reset GitHub token* with confirmation. - **Export** — code export gradient picker, "Add unique-id suffix" toggle, default export folder picker. - **Advanced** — *Open user data folder* (reveals `` in Finder/Explorer), *Reset all settings*, *Re-open onboarding*. Every control persists across restart and applies its effect immediately (no relaunch). Defaults are sensible. Reset clears everything and restores defaults. ### Fixed — Onboarding modal rendered empty + Skip/Back stayed visible in blocking mode Two compounding bugs caused the onboarding modal to look broken on first launch: 1. `iconHTML('check', …)` threw because the bare `check` glyph wasn't registered (only `check-square`). The thrown error bailed out of the intro page's `body.innerHTML` template literal, leaving the body completely empty. Added the missing icon to the registry. 2. `.mid-btn { display: inline-flex }` silently overrode the HTML5 `hidden` attribute, so `btn.hidden = true` had no visible effect. Added a global `[hidden] { display: none !important; }` rule. ## [0.2.6] — 2026-05-03 ### Added — Tab manager: window detach + split-screen (#308, #309) - **Window detach** (#308) — drag a tab outside the window bounds → it spawns into a new `BrowserWindow` carrying that single file. Origin window loses the tab. Each window persists its own tab list (new `window_id` column on `open_tabs`) and re-spawns on launch. Closing a detached window doesn't take down the origin. - **Split-screen** (#309) — drag a tab onto the left or right edge of the editor → editor splits into two independent columns, each with its own tab strip + active tab + editor. Drop indicator (vertical highlight bar) appears during drag. Drag the divider between columns to resize (default 50/50, persists). Closing the last tab in a column collapses back to single-column. New file opens go to the active column. ### Fixed — Blocking onboarding + local-folder warehouse (#314) - **Onboarding is now blocking middleware on launch.** When the workspace has no warehouse, the app cannot proceed until the user picks one. Skip / X / Esc / backdrop-click are all disabled in blocking mode. User can still re-trigger from settings later. - **New "Use a local folder" option in step 3** alongside existing-repo / new-repo. Picks any local folder via the OS dialog and persists the warehouse with `repo: 'local:'`. `repo-connect` detects the prefix and skips every git remote step — local-only warehouses don't require the workspace to be a git repo at all. ### Fixed — MCP server status sync (#316) - MCP startup now re-evaluates child health 500ms after fork and rebuilds the tray menu, so an early crash flips to 'error' instead of staying stuck on 'running'. The forked child's stderr is surfaced in the main-process console + on exit, and the alive PID is logged on success — packaged-build failures are now visible in DevTools / `electron … --enable-logging`. ## [0.2.5] — 2026-05-03 ### Added — Tab manager MVP (#287) - VSCode-style tab strip above the editor when one or more files are open. Click a tree row to open a new tab; clicking a file already open focuses its existing tab (no duplicates). - Per-tab close button + middle-click close + `Cmd/Ctrl+W` close-active. `Cmd/Ctrl+Shift+T` reopens the last closed tab. `Cmd/Ctrl+Alt+ArrowLeft/Right` cycles tabs. - Drag tabs to reorder. - Open tabs persist across restart via a new SQLite `open_tabs` table. - Window detach (#308) and split-screen (#309) deferred to follow-up issues; the IPC contract for both is sketched in `docs/desktop-tabs.md`. ### Added — Typed-notes follow-ups (#295, #296, #297, #302) - **Task-list custom view** (#295) — notes with `type: task-list` swap the markdown editor for a checklist editor (checkbox + free text + drag-reorder rows). Persists as `- [ ]` / `- [x]` markdown so the underlying file stays human-readable. - **Meeting custom view** (#296) — notes with `type: meeting` render a structured form (date, attendees chips, location, agenda + notes editors, decisions chips). Persists as YAML frontmatter + body. - **User-defined custom note types in settings** (#297) — Settings → Notes → Note types lists built-in types (read-only) and user-defined types (editable). Add custom types with a chosen id, label, icon, color, and viewKind. Stored in a new SQLite `note_types` table with built-ins seeded on first read. - **Filter strip control** (#302) — Settings → Notes → Filter strip has a master toggle, per-type visibility checkboxes, and drag-to-reorder. Strip respects all three settings; updates live with no restart. ### Fixed — release pipeline auto-publishes (#306) - The `Generate release notes from CHANGELOG` job kept failing across releases because `gh release view` returns nonzero for draft releases. Switched the wait loop to the raw `repos/.../releases` API (which lists drafts unconditionally) and added a final `gh release edit --draft=false` step. Future releases (this one included) publish without manual `gh release edit` after every tag. ## [0.2.4] — 2026-05-03 ### Fixed — onboarding wizard rebuilt + device-flow auth (#300, #301, #303) - **#300 — Onboarding modal redesigned.** The previous version had no CSS and rendered as a flat numbered list with all step content inline. Added the full `.mid-onboarding*` styling surface: frame, stepper rail (1·2·3 pills with active/done states), per-step card body, footer, error/status badges, repo list rows. Added a proper **intro page** that runs first — title + paragraph explaining what a warehouse is + three check-mark bullets + single primary CTA. Stepper hidden on intro and revealed when user clicks *Set up warehouse*. - **#301 — GitHub device flow now works end-to-end.** Default `MID_GH_CLIENT_ID` to the official `gh` CLI's public client_id (`178c6fc778ccc68e1d6a`) when the env var is unset. Surface real GitHub API error bodies — non-200 responses include status + truncated body in the error dialog instead of "Unknown error". - **#303 — Onboarding now fires on launch when no warehouse is connected.** Force-opens the modal at app start whenever the active workspace has no warehouse, regardless of the dismissed list. Dismiss still suppresses re-triggers within a session; on each launch we ask again. ## [0.2.3] — 2026-05-03 ### Fixed — packaged-build polish (#288, #289, #291) - **MCP server now auto-starts cleanly on packaged launch** (#289). `fork()` in packaged Electron uses `process.execPath` which is the Electron binary, NOT Node — without `ELECTRON_RUN_AS_NODE=1` in the fork env, the child re-launched the desktop app instead of running the MCP server. Tray label now flips to "● MCP server: running" within ~2s of launch. - **Warehouse onboarding visibility** (#288). Added a *Connect GitHub warehouse* CTA on the welcome screen (`force=true` bypasses the dismissed list, so users can always re-trigger). Added `console.debug` breadcrumbs at every short-circuit in `shouldAutoShowOnboarding()` so DevTools shows exactly why it skipped. - **Workspace-switch indexing loader** (#291). Folder-open / workspace-switch with a slow file-tree fetch now shows a delay-shown (150ms) spinner inside the sidebar tree pane. Cached folders stay instant — no flash. `openFolderDialog` also caches the pre-fetched tree so a re-open is instant. ### Added — Drag-drop file → activity-bar pinned folder v2 (#189) - Drag any file from the tree onto a pinned-folder activity-bar icon to add it to the pin's `files` list. Visual feedback distinguishes file-drop targets from reorder targets. Right-click an assigned file → *Remove from pin*. Persists via SQLite. ### Added — Spotlight visual polish (#282) - Leading search icon inside the input row, scope tabs as inline pills, two-line result rows with name + dim description. Logic from #235 unchanged. ### Added — Typed notes MVP (#255) - Notes carry a **type** (`note` default, `secret`, `task-list`, `meeting`, `reference`, `snippet`); legacy notes auto-default to `note`. Type registry at `apps/electron/notes/note-types.ts` is the single source of truth (icon + color + viewKind for each). - Notes-list rows render the type's icon + color chip (replacing the old free-form tag chips). - Horizontal type-filter strip at the top of the notes sidebar — click an icon to filter, click again to clear. - "+" new-note button now opens a chooser modal listing types (defaults to `note` if dismissed). - Right-click a note → *Change type → ...* lists every type. - **Secret type** custom view: key/value editor with reveal/copy buttons, instead of the markdown editor. Persists as YAML frontmatter `secrets: { key: value }` so the underlying `.md` stays human-readable. - Three follow-ups deferred: #295 task-list custom view, #296 meeting custom view, #297 user-defined types. ## [0.2.2] — 2026-05-03 ### Added — Settings page + 25-theme picker (#232, #233, #234) - Full-screen settings page replaces the sidebar drawer. Left rail with categories: General · Appearance · Editor · Notes · GitHub · Export · Advanced. Cmd/Ctrl+, opens it; Esc / Back returns to the previously-open document with scroll preserved. - 25-theme picker grid in Appearance, with light/dark/system mode pills. Smooth switch. - Settings UI patterns ported from `orchestra-agents/apps/components/{settings,theme}`: section cards with label / description / control rows, consistent typography against our `--mid-*` tokens. ### Added — Importer plugin system + 4 first-party importers (#246, #247, #248, #249, #250) - Plugin contract + loader at `apps/electron/importers/`. Each importer drops a folder with `index.ts` and is auto-discovered. New `mid:importers-list` IPC + chooser modal in the renderer. Sample importer + `docs/importers.md` cover the contract. - **Apple Notes** (#247) — AppleScript bridge on macOS, folder hierarchy preserved, attachments to `assets/`, frontmatter `source: apple-notes`, dry-run preview. - **Google Keep** (#248) — Takeout JSON → markdown, labels → tags, pinned + color frontmatter, checklists as `- [ ]`, attachments copied, trashed notes skipped by default. - **Notion** (#249) — exported `.md` normalized (callouts → blockquotes, toggles → `
`), database `.csv` → `/index.md` with linking table, image refs rewritten relative. - **Generic / Bear / Obsidian** (#250) — plain `.md`/`.markdown`/`.txt` folder import, wikilinks `[[Page]]` rewritten when target exists. ### Added — Spotlight parity (#235, #282) - Spotlight (Cmd+K) ported to the orchestra-agents reference design: grouped results, recents, fuzzy match preview, keyboard hint footer (#235). - Visual polish (#282): leading search icon inside the input row, scope tabs as inline pills, two-line result rows with name + dim description. ### Added — First-run GitHub warehouse onboarding (#236) - On first launch with a folder open and no warehouse, a modal walks through `gh` CLI detection, auth (Terminal command + device-flow fallback from #230), and either picking an existing repo or creating `-notes`. - "Set up warehouse…" entry added to the status-bar repo button context menu so it can be re-triggered any time. ### Added — Drag-drop file → activity-bar pinned folder v2 (#189) - Drag any file from the tree onto a pinned-folder activity-bar icon to add it to the pin's `files` list. Visual feedback distinguishes file-drop targets from reorder targets. Right-click an assigned file → *Remove from pin*. Persists via SQLite. ## [0.2.1] — 2026-05-03 ### Fixed — packaged build broken on macOS (#270, #271) - **Tray icon + MCP server** were unreachable in the v0.2.0 .dmg because `apps/electron/main.ts` resolved every runtime asset via `process.cwd()`. In dev `cwd` is the repo root; in a packaged .app it's wherever the user launched from, so `media/brand/iconTemplate.png`, `out/mcp/server.js`, and `build/icons/16.png` all 404'd. New `bundleAsset(...segments)` helper resolves both dev and packaged paths via `__dirname` walk + transparent `app.asar.unpacked` fallback. Replaces every `process.cwd()` lookup. - **Bundle config** — added `out/mcp/**`, `media/brand/**`, `build/icons/**` to `package.json#build.files`. `out/mcp/**` and `node_modules/better-sqlite3/**` are `asarUnpack`'d so `fork()` can spawn the MCP server and the native sqlite binary loads. - **Universal macOS DMG** (#271) — switched `mac.target` from per-arch (arm64 + x64) to a single `universal` slice. No more Rosetta-deprecation warning when an arm Mac downloads the x64 DMG; one installer that runs natively on both architectures. - **Tray "Check for Updates…"** entry. Label flips to *Downloading v…* → *Restart and install v…* as the auto-updater progresses; local builds (no `app-update.yml`) get a friendly *only available in published releases* dialog. ### Fixed — release pipeline (#269) - vsix job was racing the electron jobs to upload and failing with `release not found`. Made the upload idempotent (creates the release as a draft if it doesn't exist) and decoupled the release-notes job from the vsix job so a vsix retry doesn't re-block release-notes. ### Added — Mac App Store target (#266) - New opt-in `mas` target for App Store distribution (sibling to the regular DMG). Sandboxed entitlements, 3rd-party Mac Developer signing, separate workflow file gated on `vars.MAS_ENABLED=1`. Regular DMG releases unchanged. ### Added — Right-side outline rail (#252) - New table-of-contents rail on the right side of the preview, listing every heading in the active markdown file with indent matching heading level. Click → scroll to that section. IntersectionObserver highlights the current section as you scroll. Toggle from the status-bar *Outline* button or `⌘⇧L`. Hidden in edit-only mode and during PDF export. Persists across restart. ## [0.2.0] — 2026-05-03 ### Added — Desktop app (Electron) — release engineering - **GitHub Releases auto-updater** (#264) — `electron-updater` is wired against `package.json#build.publish`; running clients check on launch, download the matching artifact in the background, prompt the user to restart-and-install or defer to next-launch. Channels (`stable` / `beta`) honour `MID_CHANNEL`; pre-release tags (e.g. `v0.2.0-rc.1`) are auto-flagged so the stable channel skips them. Docs: `docs/desktop-auto-update.md`. - **macOS hardened-runtime signing + notarization** (#265) — `package.json#build.mac` now sets `hardenedRuntime: true`, points at a new `build/entitlements.mac.plist` with the minimum capabilities Electron + V8 + better-sqlite3 + the file picker need, and disables the post-sign Gatekeeper assess step. `release.yml` already exports the five required Apple secrets when present. Docs: `docs/desktop-mac-signing.md`. - **macOS .zip target** alongside DMG so `electron-updater` can apply blockmap-based delta updates (without it the DMG-only build couldn't auto-update on Mac). ### Added — Desktop app (Electron) — data layer & exports - **Local SQLite store** (#239) for settings, recent files, pinned folders, workspaces, GitHub warehouses, and an export-history audit log. Database lives at `/mid.sqlite`. One-shot migration from `state.json` runs in a single transaction on first launch and renames the source file to `state.json.migrated`. Renderer-facing IPC (`mid:read-app-state` / `mid:patch-app-state`) is unchanged — all migration cost was paid in the main process. Two new IPCs: `mid:record-export`, `mid:list-export-history`. Docs: `docs/sqlite-store.md`. - **Native module rebuild on install** (#258) — `postinstall` hook runs `electron-rebuild` for `better-sqlite3` so its native binary matches Electron's Node ABI. Without it, fresh installs crashed on launch with `NODE_MODULE_VERSION` mismatch. - **Unique-id export filenames** (#238) — every export (PDF / DOCX / HTML / PNG / TXT / MD / code-PNG / mermaid SVG+PNG / table CSV+JSON+XLSX+PNG) is suffixed with an 8-char id, so re-exporting never overwrites a prior file. Docs: `docs/export-filenames.md`. - **Code-block PNG export** (#237, #260) — fixed blank-image regression caused by `opacity: 0` on the temp wrapper bleeding into html-to-image's clone; bumped backdrop padding to 96px and inner pre padding to ~28px so the snapshot looks like a Carbon screenshot. Docs: `docs/desktop-export-image.md`. - **Table PNG export** (#261) — DataTable export menu has a new "Export as PNG" item; snapshots the filtered + sorted view inside the same macOS-style chrome the code blocks use. ### Added — Desktop app (Electron) — UX - **Cmd+K spotlight closes on backdrop click** (#242). Esc still works. - **DataTable toolbar / header gap fix** (#241) — scoped the markdown-table base styles to `:not(.mid-dt-table)` so they stop leaking into the upgraded DataTable. - **Sidebar tree icons bumped to 18px** + **macOS tray icon re-rasterized at 22pt** (#253, #254) — match JetBrains-size and macOS menu-bar conventions respectively. ### Added — VSCode extension — v2.0: Per-token highlight.js theme mapping (#51) - All 25 bundled themes now ship with a coherent code-highlight palette tuned against their chrome (keyword, built-in, string, number, comment, fn, type, variable, operator, params, meta, regex, title, tag, attribute) - New `packages/core/src/themes/hljsCss.ts`: pure helpers `deriveHljsTokens` (algorithmic mapping from chrome palette → token colours), `hljsTokensFor` (curated overrides + derived fallback), `hljsCssFor` (emits the CSS rules) - Curated palettes for the marquee themes: github-light/dark, dracula, one-dark/light, monokai, solarized-light/dark, tokyo-night, nord, gruvbox-light/dark, rose-pine, cobalt2 — using each ecosystem's canonical token colours - Algorithmic mapping for the rest (Ayu, Material, Night Owl, Oceanic Next, Snazzy, Palenight, Tokyo Night Light, Nord Light) — derived from the theme's own link / accent / fgMuted so the highlight palette never fights the chrome - Webview pipeline (`webviewBuilder.ts`) emits the per-theme hljs CSS right after the existing `:root[data-theme=…]` chrome override - Published static site (`buildSiteAssets`) appends the same per-theme rules after the bundled hljs base stylesheet so deployed sites match local rendering - Updated `docs/themes.md` with a new "Per-token highlight.js palette (#51)" section explaining derivation, curation, where it lands, and what's still up for grabs - 9 new unit tests across `deriveHljsTokens` (link → keyword, accent → built-in, fgMuted → comment), `hljsTokensFor` (curated overrides for github-light + dracula, derived fallback), `hljsCssFor` (every standard hljs class present, .hljs base color uses fg, every bundled theme emits valid CSS), and parity (every theme resolves to a fully-populated HljsTokens object) ### Added — v2.0: Slideshow live reload (#50) - `Mark It Down: Slideshow: Preview Local` no longer needs to be re-run on each edit — the preview panel hot-reloads on every keystroke (debounced 120 ms) and on explicit save (no debounce); slide position is preserved across rebuilds - New `liveReload?: { initialIndex? }` option on `slideshowGenerator` injects a small bridge script: `acquireVsCodeApi`, `Reveal.on('slidechanged' / 'fragmentshown' / 'fragmenthidden')` posts `slideshow.position` back to host; `Reveal.on('ready')` calls `Reveal.slide(h, v, f)` with the baked-in initial index - `SlideshowManager` now tracks open preview panels in a `Map`; first call creates the panel + wires document/save listeners, subsequent calls reveal + rebuild the existing one (no duplicate panels per file) - Reveal's URL hash routing is disabled in live mode (it would conflict with the position-restore script); still honoured outside live mode (publish path, embed) - 5 new unit tests covering bridge presence/absence, hash toggle, initial-index baking, missing-index fallback to null ### Added — v2.0: Internationalization (#49) - Manifest strings (command titles, view names, settings descriptions, the activity-bar container, the welcome message) externalised into `package.nls.json` (English source) + `package.nls..json` per locale; runtime strings (status bar labels, empty-state hints, exporter notifications) externalised into `l10n/bundle.l10n.json` + `l10n/bundle.l10n..json` - 4 locales beyond English shipped: **es** (Spanish), **fr** (French), **de** (German), **ar** (Arabic — RTL handled natively by VSCode) - Thin `src/i18n/t.ts` wrapper around `vscode.l10n.t()` for stable call sites - New `scripts/check-i18n.mjs` parity checker — verifies every locale file has exactly the same keys as the English source; wired into `.github/workflows/ci.yml` so missing/stale translations fail the build - New `npm run check-i18n` script for local use - `package.json` `l10n` field points at `./l10n` so VSCode resolves the runtime bundle automatically - 11 new unit tests in `tests/unit/i18n/parity.test.ts`: at-least-4 locales beyond English (per family), per-file exact-key parity, every value is a non-empty string ### Added — v2.0: ePub export (#48) - Two new commands: `Mark It Down: Export to ePub` (single-file, one chapter per book) and `Mark It Down: Export Category as ePub` (multi-chapter book bundling every note under a category path, sorted oldest-first by `updatedAt`) - Bundled minimal EPUB 3.0 generator in `packages/core/src/epub/` — zero new runtime deps, just Node's `zlib`. Ships a 110-line ZIP writer (stored + deflate, precomputed CRC32 table, valid EOCD) plus a templated builder (container.xml, content.opf, nav.xhtml, toc.ncx, styles.css, chapter XHTML) - XHTML hardening for marked output: self-closes `
`, `
`, `` so EPUB-strict readers parse cleanly - Frontmatter is stripped before each chapter renders (reuses the parser from #46) - New settings: `markItDown.epub.author`, `markItDown.epub.publisher`, `markItDown.epub.coverImage` (absolute or workspace-relative path to a PNG/JPEG cover; missing → silently dropped) - Category bundler reuses the nested-categories prefix-match (`Reference` collects `Reference` + `Reference/Postgres` + `Reference/Networking/CIDR` + …) - Explorer-context menu and command-palette entries; `markItDown.exportEpub` keyed off `.md`/`.markdown`/`.mdx` - 13 new unit tests across the zip writer (CRC32 known value, empty-input, single stored entry, deflate round-trip, mimetype-first ordering, EOCD record) and the builder (mimetype first, all required structural files, one chapter file per chapter, chapter content, content.opf manifest entries + metadata, optional cover image embedding, XML escaping in titles) ### Added — v2.0: Multi-warehouse routing (#47) - New `markItDown.warehouse.routes` setting accepts an array of `{categoryPrefix, repo, branch?, subdir?}` rules; notes whose category matches a prefix sync to that route's repo, everything else falls through to the default `markItDown.warehouse.repo` - Pure router lives in `packages/core/src/warehouse-routing/`: builds resolved routes from rules, validates inputs (rejects empty / malformed / duplicate-prefix rules with reasons logged), inherits branch + subdir from the default config, sorts routes by descending prefix specificity so deeper rules win, exposes a per-route predicate - `WarehouseSync.pull` / `planPush` / `push` accept an optional `predicate` so each call only handles its route's notes; `planPush` flags remote notes whose category no longer matches the route as deletes (so re-categorising a note from one route to another is handled gracefully — added on the new repo, deleted from the old) - `WarehouseManager` iterates every enabled route per `syncNow` / `pullCommand` / `runAutoPush`, with separate first-push confirmation per repo (`firstPushFlagKey` already keys on repo so each new warehouse gets its own one-time dialog) - Status bar collapses to `@` for a single route, `N warehouses` for multiple - `Open on GitHub` shows a Quick Pick when more than one route is enabled - 10 new unit tests covering route parsing (no-rules, no-default, every rejection branch, branch/subdir inheritance + override) and routing semantics (prefix match, default exclusion, sibling non-bleed) ### Added — v2.0: Per-page slug overrides via frontmatter (#46) - Notes can declare a `slug:` in a YAML frontmatter block at the top of the body — published page goes to `notes/.html` instead of the default `notes/-.html` - New `packages/core/src/frontmatter/index.ts` — tiny zero-dep parser for `---`-fenced blocks, supports scalars (string / number / boolean), inline lists, quoted strings, comment lines, BOM, missing-fence safety - `validateSlug` rules: `^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$` and ≤48 chars; anything else (uppercase, whitespace, slashes, leading/trailing dash, over-length) silently falls back to the title-derived id-suffixed URL - Slug collision detection in `PublishManager.collectAll`: first claimant keeps the clean URL, conflicting note(s) fall back to id-suffixed URL, warehouse log gets a `slug collision` entry, and a one-shot `showWarningMessage` surfaces it - `publishCurrent` (the per-file path) also strips frontmatter + honours slug override for arbitrary markdown files - 15 new unit tests across the parser (no-fence, fenced + stripped, scalar types, lists, comments, missing closing fence, BOM tolerance, leading blank line) and `validateSlug` (lowercase-dash, digits, rejected patterns, length cap, non-string input) ### Added — v2.0: Nested category hierarchies (#45) - Category names accept `/` as a hierarchy separator — `Reference/Postgres`, `Reference/Postgres/Indexing`, `Daily/2026-04` all coexist as plain category strings (no schema migration) - Notes sidebar renders categories as a true tree: each scope shows root segments, expanding a category shows its child segments first then notes that live at the exact path; tooltip surfaces the full path on hover - New `packages/core/src/categories/` package: pure helpers `parseCategoryPath`, `joinCategoryPath`, `categoryHasPrefix`, `rootCategories`, `childCategoriesAt` — segment-aware so siblings don't bleed together (`References/Foo` does not match prefix `Reference`) - MCP `list_notes` accepts `categoryPrefix` (matches the path itself or anything underneath it); existing `category` filter still does exact match. Trailing slashes on the prefix are stripped before comparison. - Tree provider uses the new helpers — flat categories render unchanged (a one-segment path is just a leaf folder) - 14 new unit tests covering parse / join / prefix matching (exact, descendant, sibling rejection, empty wildcard) / root collection / child collection at root + at depth + grandchild de-dup + parent-skip ### Added — v2.0: Note attachments (#44) - Each note can carry binary attachments stored at `/notes/-attachments/`; the dir is created lazily on first attachment and removed when the note is deleted - Drag-drop one or many files onto the editor (view or edit mode) — webview reads each via FileReader, base64-encodes, posts `attachUpload` to the host, store sanitises + de-duplicates the filename, then a relative markdown reference (`![]()` for images, `[]()` otherwise) is appended at the end of the note - New `packages/core/src/attachments/` package: pure helpers for filename sanitisation (path-traversal safe, alphanumeric+dot+dash+underscore only, 96-char cap that preserves extension), collision resolution (`a.png` → `a-1.png`, `a-2.png`, …), image-extension detection, markdown-reference emission - `NotesStore` extensions: `attachmentsDirUri`, `attachmentUri`, `listAttachments`, `addAttachment`, `deleteAttachment`; `importNote` now accepts an optional `attachments` payload so warehouse pull can materialise binaries - Warehouse sync: `materializeScope` copies each note's attachment dir verbatim, indexes filenames as `attachments?: string[]` in `_index.json`, and deletes the dir on note removal; pull side calls `readRemoteAttachments` and threads them through `importNote` - Publish: `collectAll` returns `{ pages, attachments }`; `runBuildAndPush` copies each attachment to `notes/-attachments/` next to its rendered page so relative links resolve naturally on GitHub Pages - 19 new unit tests across the pure helpers (sanitisation incl. path traversal, length cap with extension preservation, collision resolution with + without extension, image detection across 7 cases, markdown emission for image / non-image / aliased) ### Added — v2.0: Wiki-links + backlinks (#43) - Markdown gets `[[note title]]`, `[[note title|alias]]`, and `[[note title#anchor]]` syntax — resolved against the live notes corpus - New `packages/core/src/wikilinks/` package: pure parser (skips fenced + inline code), case-insensitive resolver with ambiguous + broken statuses, source rewriter that injects sanitised anchor HTML before marked runs - Webview renders wiki-links with three visual states — solid accent (resolved), warning colour (ambiguous → Quick Pick on click), red dotted (broken → "Create note?" prompt) - New **Backlinks** sidebar view next to **Notes** — for the active note, lists every other note linking to it, with verbatim wiki-link snippet in the description - `BacklinksIndex` rebuilds on store changes (debounced 300 ms) + on demand via `Mark It Down: Refresh Backlinks` - New hidden command `markItDown.notes.createWithTitle` — invoked by broken-link clicks, jumps the user through scope + category pickers prefilled with the bracketed title - 17 new unit tests across parser (code-region masking, alias/anchor split, multi-link lines) and resolver (case-folding, ambiguity, self-link suppression, alias preservation in backlinks) ### Added — v2.0: Tags (#42) - New optional `tags?: string[]` field on `NoteMetadata` — multi-tag-per-note, orthogonal to `category` - Tags are normalized: lowercase, alphanumeric + dashes, deduped + sorted (e.g. `Postgres Tuning, design` → `["design", "postgres-tuning"]`) - `NotesStore.setTags(id, tags[])` + `tagsInUse(scope?)` helpers - 2 new commands: - `Mark It Down: Edit Tags` — context menu on a note + command palette; opens an InputBox prefilled with current tags - `Mark It Down: Filter by Tag` — Quick Pick over all tags in use → Quick Pick of matching notes → opens the chosen one - Notes tree renders up to 3 tag badges in the description (`#design #postgres-tuning · 14:32`); full tag list in the tooltip - MCP `list_notes` accepts an optional `tag` filter (case-insensitive); `create_note` + `update_note` accept a `tags: string[]` patch - `MdNote` interface (MCP-side) gains the same `tags?` field ### Added — v1.2 polish: search across notes (#41) - New `packages/core/src/search/searcher.ts` — pure-function fuzzy search (no deps), shared by sidebar + MCP. Per-token scoring: exact title (+10), partial title (+5), category (+3), body occurrences (+1, capped at 5). Tie-breaks by recency. - **Sidebar search**: `Mark It Down: Search Notes` command — input box → fuzzy match → Quick Pick → Enter opens the chosen note. Wired into the Notes view title bar. - **MCP `search_notes` tool**: `{ query, limit? }` → ranked hits with `id`, `title`, `category`, `scope`, `updatedAt`, `score`, `snippet`. - **Claude plugin**: new `/mid:search` skill (7 skills total in the bundle now). - **Published-site search**: each site ships `assets/search-index.json` (Lunr 2 + a doc map with title + 200-char snippet). Header search input lazy-loads the index on first keystroke; debounces 200ms; up to 12 hits. - 11 new unit tests covering the searcher (tokenize, title/body/category scoring, exact vs partial title boost, multi-token sum, limit, recency tie-break, no-match, snippet length cap) ### Added — v1.2 polish: pre-release / beta channel (#40) - New `markItDown.updates.channel` setting (`"stable"` default, `"beta"` opt-in) - VSCode poller: stable channel hits `/repos/.../releases/latest` (GitHub auto-skips pre-releases); beta channel hits `/repos/.../releases?per_page=20` and picks the newest non-draft including pre-releases - Electron `autoUpdater.channel` / `allowPrerelease` driven by the `MID_CHANNEL=beta` env var (mirror of the VSCode setting) - Release workflow now matches both `v*.*.*` (stable) and `v*.*.*-*` (pre-release: `-rc.1` / `-beta.2` / etc.) tag patterns - Workflow's release-notes job auto-flips the GitHub release's `prerelease` flag based on whether the tag has a hyphen — stable tags get `prerelease: false`, pre-release tags get `prerelease: true` - New "Pre-release / beta channel" section in `docs/auto-update.md` walking through how the two channels diverge ### Added — v1.2 polish: side-by-side conflict-resolution UI for warehouse (#39) - New `ConflictRegistry` collects diverged notes during sync (was previously just a log warning) - New `ConflictPanel` webview shows local vs remote side-by-side per note with `Keep local` / `Replace with remote` / `Skip` actions; live-refreshes as the registry changes - Status bar's `conflict` state now opens the panel on click (other states still open the log channel) - New command: `Mark It Down: Warehouse: Resolve Conflicts` - `Replace with remote` calls `NotesStore.importNote` with the remote content + remote `updatedAt` - Strict CSP scoped to the panel webview; per-button `aria-label`s; dark/light theme follows VSCode `--vscode-*` variables - F9 (#10) docs future-work seed for "side-by-side conflict UI" — resolved ### Added — v1.2 polish: accessibility audit + remediations (#38) - Mode toggle buttons (View / Edit): `aria-pressed` toggled in sync with `.active` class; `aria-label` overrides emoji-only labels; toolbar wrapper gains `aria-label="Mark It Down view mode"` - Sortable table headers: `role="columnheader"` + `tabindex="0"` + `aria-sort` updated on sort + Enter/Space keyboard handler + visible `:focus` outline (sort was previously mouse-only) - Sort indicator span marked `aria-hidden` so screen readers don't double-announce - Per-table export buttons get `aria-label="Export table N as CSV/TSV/Excel"` so position is announced when there are multiple tables on a page - Per-table toolbar gets `role="toolbar"` + `aria-label` - `:focus-visible` outline added for `.toolbar`, `.mid-table-actions`, and `.mid-controls` button groups (was invisible during keyboard nav) - Warehouse status-bar item: stable `id` + descriptive `name` + `accessibilityInformation.{label, role}` updated on every state transition — screen readers now announce e.g. "Mark It Down warehouse: Notes synced, fadymondy/notes@main" - New `docs/accessibility.md` — full audit log with per-finding fix or follow-up link, scope-of-pass table, what's already covered by VSCode itself, and how to run the audit yourself - Deferred (with explicit follow-ups in `docs/accessibility.md`): full WCAG AA sweep across all 25 themes, slideshow panel focus trap, high-contrast variants, live-region announcements for sync state changes, skip-to-content in published HTML ### Added — v1.2 polish: opt-in telemetry via Sentry (#37) - New `src/telemetry/` module: `TelemetryClient` + `sanitize` helpers - **Default off.** First activation surfaces a one-time consent toast (`Enable / Keep off / Learn more`) — choice persists in `globalState[markItDown.telemetry.consentShown]` - 2 settings: `markItDown.telemetry.enabled` (default `false`) + `markItDown.telemetry.dsn` (default empty — official build ships without a DSN; events stay local until the user points it at their own Sentry project) - 1 command: `Mark It Down: Telemetry: Send Test Event` for verification - PII filter at source: workspace paths → ``, home dir → `~`, extension dir → ``, system temp → ``. Strings truncated at 4096 chars. Applied to error events AND breadcrumbs via Sentry `beforeSend` / `beforeBreadcrumb`. - Random per-launch session id (16-char hex, not persisted) tagged on every event - `release` tag from `package.json#version`; `tracesSampleRate: 0` (no perf traces collected) - 9 unit tests covering the sanitizer (path replacement, longest-anchor-first, deep walk, length truncation, session-id format + uniqueness) - Full reference docs at `docs/telemetry.md` — consent flow, what's sent, what's NEVER sent, verification recipe ### Added — v1.2 polish: packages/core/ extraction — first cut (#36) - New `packages/core/src/` directory holds the `vscode`-API-free shared modules: - `themes/` — the 25 palette definitions + helpers (moved from `src/themes/`) - `markdown/tokens.ts` — `tokenize` + `inlineToText` (moved from `src/exporters/markdownTokens.ts`) - `markdown/renderer.ts` — **new** consolidated `renderMarkdown` + `applyMermaidPlaceholders` that both `src/webview/main.ts` (VSCode webview) and `apps/electron/renderer/renderer.ts` (Electron renderer) now use - `secrets/scanner.ts` — token regex set (moved from `src/warehouse/secretScanner.ts`) - `semver/compare.ts` — `compareSemver` + `parseSemver` (moved from `src/updates/updateChecker.ts`) - All previous import sites stay working via thin re-export shims in the original locations (no churn at call sites; tests still green at 71/71) - `tsconfig.json` widened to include both `src/` and `packages/core/src/` under a project-root `rootDir`; `package.json#main` updated to `./out/src/extension.js` to match the new output layout (`out/src/...` + `out/packages/core/src/...`) - Documented what's deferred in `packages/core/README.md`: notes/warehouse/publish/slideshow/MCP-install all touch `vscode.*` and need interface abstractions before they can land; Electron's IPC bridge currently re-implements small subsets that core could absorb in a future ship - Webview + Electron renderer now share one `renderMarkdown` implementation — the ~30 LOC mermaid-placeholder duplication that #13 (Electron first-cut) called out is gone ### Added — v1.1 hardening: MCP IPC channel for active-editor introspection (#35) - The 2 stub tools from F8 (`get_active_markdown` and `list_open_md`) now actually work - New cross-platform IPC channel: Unix-domain socket on macOS/Linux at `${globalStorageUri}/mid-mcp.sock`, named pipe on Windows at `\\.\pipe\mark-it-down-` (the hash makes multiple installs collision-safe) - `src/mcp/ipcProtocol.ts` — newline-delimited JSON request/response types + `ipcEndpoint(dir)` helper - `src/mcp/ipcServer.ts` — `McpIpcServer` listener wired into extension activation; cleans up stale sockets on POSIX startup; per-connection request handler covers `ping` / `get_active_markdown` / `list_open_md` - `src/mcp/ipcClient.ts` — connects + sends + closes per request (no pool needed at MCP volume); 3s timeout - `src/mcp/server.ts` — `--ipc-sock ` CLI arg; both tools route through `IpcClient` and gracefully degrade with a clear error when the channel is unreachable - `src/mcp/installCommand.ts` — auto-passes `--ipc-sock` per-install so users don't have to know the path - 4 new unit tests + 3 round-trip integration tests covering the protocol with a stand-in server (71 tests total, all passing) - `docs/mcp-server.md` updated to remove the deferred caveat for both tools, document the IPC architecture, and show the response shape for each ### Added — v1.1 hardening: VSCode Marketplace publisher setup runbook (#34) - `package.json` extended with marketplace-friendly metadata: `bugs.url`, `categories` adds "Notebooks", `keywords` adds preview/publish/slideshow/github-pages, `galleryBanner` (`#0d1117` dark), `qna: marketplace` - `media/marketplace/` placeholder directory with a README documenting the 6 screenshots needed for the listing (filename convention, size budget, capture instructions) - `docs/releasing.md` gains a 7-step "Setting up the VSCode Marketplace publisher" section: publisher creation at marketplace.visualstudio.com → Azure DevOps PAT → repo secret → first manual `vsce publish` → Open VSX mirror recommendation → screenshot guidance → token rotation cadence - The release workflow's `vsce publish` step is already conditional on `VSCE_PAT` being set (from F14 #28) — gracefully no-ops until the user adds the secret - **User action required** to actually go live: create the publisher account + add `VSCE_PAT` secret + first manual publish per the runbook ### Added — v1.1 hardening: macOS code signing + notarization wired (#33) - `.github/workflows/release.yml` env block now references `MAC_CERTS`, `MAC_CERTS_PASSWORD`, `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID` secrets — populated only on macOS runners that have them, electron-builder gracefully falls back to unsigned builds when absent - `docs/releasing.md` gains a complete "Setting up macOS code signing" section walking through cert creation in Xcode → `.p12` export → base64 → app-specific password → team ID → repo secrets, plus a verification + cert-rotation note - `docs/auto-update.md` Code Signing section updated to point at the new releasing.md flow - **User action required after merge** to actually enable signing: add the 5 GitHub repo secrets per the runbook. Until then, releases still ship unsigned macOS DMGs (functional but no auto-update on Mac). ### Added — v1.1 hardening: VSCode integration tests via @vscode/test-electron (#32) - New `tests/integration/` with the runner (`runTest.ts`), Mocha test-suite loader (`suite/index.ts`), and 5 happy-path tests covering: extension presence, activation without errors, every contributed command registered after activation, custom-editor `openWith` for a `.md` file resolves cleanly, configuration namespaces (`theme`, `startMode`, `mermaid.enabled`, `notes.categories`) all readable. - `tsconfig.integration.json` outputs to `out/integration/`; `npm run compile:integration` + `npm run test:integration` scripts wired - New CI job (`integration`) runs after the lint-and-build matrix, on ubuntu only, downloads VSCode via `@vscode/test-electron`, invokes the suite under `xvfb-run` - Adds `.vscode-test-user-data/` + `coverage/` to `.gitignore` ### Added — v1.1 hardening: vitest harness + 65 initial unit tests (#31) - New `vitest.config.ts` with a `vscode` module alias pointing at `tests/__mocks__/vscode.ts` so src/ files that import vscode don't break the test runner - 65 unit tests across 6 files covering the pure-function modules: `markdownTokens` (tokenize + inlineToText), `secretScanner` (9 token patterns + redaction), `warehouseConfig` (slugify, repoSlug, repoUrl, repoWebUrl, scopeDir), `themes` (25 palettes + findTheme + paletteToCss), `compareSemver` + `parseSemver` (exported from `updateChecker`), `markdownToTxt` (heading / inline / list / code / table / blockquote / hr handling) - Scripts: `npm test`, `npm run test:watch`, `npm run test:coverage` - CI workflow now runs unit tests + uploads a v8 coverage report as an artifact (no threshold enforced yet — first baseline) - Exported `compareSemver` + `parseSemver` from `src/updates/updateChecker.ts` for testability (no behavior change) ### Added — v1.1 hardening: ESLint flat config + PR-time CI (#30) - New `eslint.config.mjs` (flat config, ESLint 10) covering `src/`, `apps/electron/`, with separate Node + browser global sets per file group - `npm run lint` and `npm run lint:fix` scripts wired and pass on `main` HEAD (0 errors, 3 minor warnings) - New `.github/workflows/ci.yml` — runs `npm ci && npm run lint && npm run compile && npm run compile:electron && npm run build:claude-plugin` on every PR + push to main, on a `[ubuntu-latest, macos-latest]` matrix - Cancels superseded runs via `concurrency.cancel-in-progress: true` - 3 case-block scope errors fixed in the export pipeline (`exportTxt.ts`, `exportDocx.ts` x2) ### Added — Phase 0.14: Auto-update for both surfaces (#28) - **VSCode extension**: in-extension `UpdateChecker` polls `GET /repos/fadymondy/mark-it-down/releases/latest` once per launch + every 6h; surfaces a notification with `Open Release` / `View Changes` / `Later` actions when a newer version is published. Doesn't re-notify for the same version. Never auto-installs (security smell). - **VSCode extension — What's New**: on first launch after an update, a one-time toast surfaces with a `View What's New` action that opens the GitHub release page. First-ever install is silent. - **Manual check**: `Mark It Down: Check for Updates` from the command palette. - **Setting**: `markItDown.updates.checkOnLaunch` (default `true`). - **Electron app**: `electron-updater` wired with `provider: github`. App checks on launch, downloads in background, prompts the user to install on next quit OR restart now. `autoInstallOnAppQuit` defaults to `false` — explicit user opt-in for silent installs. - **Help → Check for Updates…** menu item in the Electron app. - **package.json#build.publish** block configures the GitHub provider for electron-updater. - **`.github/workflows/release.yml`**: tag-push pipeline (`v*.*.*`) builds Electron installers (mac/win/linux matrix) + `.vsix` (vsce package) + auto-publishes to Marketplace if `VSCE_PAT` is set; release body is extracted verbatim from the matching CHANGELOG `## [X.Y.Z]` section. - **Docs**: `docs/auto-update.md` (what users see) + `docs/releasing.md` (full release runbook with rollback recipe + common failures table). - **macOS auto-update caveat**: requires signed + notarized DMG. Wire `CSC_LINK` + Apple credentials into the release workflow's env block when ready; until then unsigned macOS builds install fine but can't apply updates. ### Added — Phase 0.13: Claude Code plugin (mark-it-down-claude) (#14) - New `plugins/mark-it-down-claude/` packages the bundled MCP server, 6 user-invocable skills, and 3 specialist sub-agents into a single Claude Code plugin - Skills: `/mid:new-note`, `/mid:list-notes`, `/mid:open`, `/mid:slideshow`, `/mid:publish`, `/mid:warehouse-status` - Agents: `notes-curator` (audit + organize warehouse), `slideshow-designer` (restructure markdown into talk-ready decks), `note-summarizer` (single-note / category / theme digests) - `.mcp.json` ships the bundled server entry — `${CLAUDE_PLUGIN_ROOT}/bin/server.js --notes-dir ${MID_NOTES_DIR}` — defaults `MID_NOTES_DIR` to `~/.mark-it-down/notes` for standalone use; users with the VSCode extension override to point at the extension's globalStorage notes dir so both clients see the same store - New `npm run build:claude-plugin` script: re-runs `compile:mcp` and copies `out/mcp/server.js` → `plugins/mark-it-down-claude/bin/server.js` - README in the plugin dir covers install (local + marketplace), per-OS notes-dir defaults, skill/agent reference - Plugin version is independent of the VSCode extension (manifest at `0.1.0`); for marketplace distribution, the plugin directory is intended to be synced to its own repo `fadymondy/mark-it-down-claude` ### Added — Phase 0.12: Standalone Electron app — first cut (#13) - New `apps/electron/` ships a working standalone desktop app: main process + sandboxed renderer + preload IPC bridge + native menus - Renderer reuses marked + marked-highlight + highlight.js + mermaid + DOMPurify (same versions as the VSCode webview); standalone palette with system light/dark switch via `nativeTheme` - File menu: Open Markdown… (Cmd/Ctrl+O), Save (Cmd/Ctrl+S); standard Edit, View, Help menus; macOS app menu when running on macOS - External links open in the OS default browser via `shell.openExternal` - New npm scripts: `compile:electron` (tsc main+preload + esbuild renderer + copy HTML/CSS), `dev:electron` (compile + launch with DevTools), `dist:electron` (electron-builder for mac DMG + win NSIS + linux AppImage/deb) - electron-builder config under `package.json#build` with `extraMetadata.main` override so the same package.json works for both VSCode and Electron - **Deferred and documented in [docs/electron-app.md#whats-deferred-and-why](docs/electron-app.md#whats-deferred-and-why)**: full `packages/core/` extraction, CodeMirror in Edit mode, notes sidebar, warehouse, publish, slideshow, MCP install command on the Electron side, file watching, multi-tab/window, code signing/notarization, CI matrix. v0.12 is a parallel renderer with ~200 LOC of intentional duplication; the extraction is its own multi-day refactor tracked separately. ### Added — Phase 0.11: Slideshow export (#12) - Convert any markdown into a [reveal.js](https://revealjs.com) slideshow — `---` for horizontal slide breaks, `--` for vertical sub-slides, optional YAML frontmatter for `theme` / `transition` / `title` / `speakerNotes` - `Mark It Down: Slideshow: Preview Local` opens a webview panel beside the editor with reveal.js + mermaid + highlight.js loaded from JSDelivr at view time (CSP relaxed scoped to that panel only) - `Mark It Down: Slideshow: Publish` reuses the F10 publish pipeline — pushes a single self-contained HTML to `//slides/.html`, surfaces an info toast with Open / Copy URL actions - `Mark It Down: Slideshow: Copy Share URL` and `Slideshow: Export to PDF` (the PDF command currently surfaces a defer-notice pointing at reveal's built-in `?print-pdf` browser print mode) - 3 new settings: `markItDown.slideshow.theme` (12 reveal themes), `.transition` (6 styles), `.includeSpeakerNotes` - `Notes:` block at the end of any slide body becomes reveal speaker notes (`