diff --git a/TODO.md b/TODO.md index 6909ac36..9394c55f 100644 --- a/TODO.md +++ b/TODO.md @@ -22,14 +22,14 @@ the owner's OS user and can never be granted. Indirection there really is accide ## Themes -**108 open, 33 closed**, across 35 themes. +**109 open, 33 closed**, across 35 themes. | Theme | Open | Closed | | --- | --- | --- | | [Affordances](todo/affordances/) | 2 | 0 | | [Agents coordinating agents](todo/agent-coordination/) | 5 | 0 | | [Speaking into a session from the backend](todo/agent-delivery/) | 1 | 1 | -| [Backburner](todo/backburner/) | 3 | 0 | +| [Backburner](todo/backburner/) | 4 | 0 | | [Background jobs](todo/background-jobs/) | 2 | 1 | | [Calendar and contacts](todo/calendar/) | 1 | 0 | | [Chat interface](todo/chat/) | 1 | 0 | @@ -71,6 +71,7 @@ the owner's OS user and can never be granted. Indirection there really is accide ## Newest ten +- `2026-09-04` **M3E Canvas as a plugin — sketch a UI, hand the prompt to the agent** — [backburner/m3e-canvas-as-a-plugin](todo/backburner/m3e-canvas-as-a-plugin.md) - `2026-08-29` **Officer blocks its own event loop for ~0.9s re-parsing a live transcript, and everything it serves stalls with it** — [known-bugs/officer-blocks-its-event-loop-parsing-transcripts](todo/known-bugs/officer-blocks-its-event-loop-parsing-transcripts.md) - `2026-08-27` **Turn an ordinary chat into a commander or a bot, carrying its context along** — [agent-coordination/turn-a-chat-into-a-commander-or-a-bot](todo/agent-coordination/turn-a-chat-into-a-commander-or-a-bot.md) - `2026-08-27` **The calendar API cannot express repeating events, reminders, invitees or a cancelled occurrence** — [calendar/the-json-api-cannot-express-recurrence-alarms](todo/calendar/the-json-api-cannot-express-recurrence-alarms.md) @@ -80,4 +81,3 @@ the owner's OS user and can never be granted. Indirection there really is accide - `2026-08-27` **Setting up a phone for calendar and contacts should be one link inside Officer, not a manual account form** — [mobile-apps/setting-up-a-phone-should-be-a-link-in-the-app](todo/mobile-apps/setting-up-a-phone-should-be-a-link-in-the-app.md) - `2026-08-27` **Marketplace: browse by tag — filter list, card grid, detail with a back button** — [plugin-ecosystem/marketplace-browse-by-tag](todo/plugin-ecosystem/marketplace-browse-by-tag.md) - `2026-08-25` **An affordance: a scoped agent warns when a request lands outside its scope** — [affordances/an-affordance-a-scoped-agent-warns-on-the](todo/affordances/an-affordance-a-scoped-agent-warns-on-the.md) -- `2026-08-25` **Commit signing is inconsistent across harnesses — Claude names itself, opencode names nothing** — [agent-coordination/commit-signing-is-inconsistent-across-harnesses](todo/agent-coordination/commit-signing-is-inconsistent-across-harnesses.md) diff --git a/todo/backburner/m3e-canvas-as-a-plugin.md b/todo/backburner/m3e-canvas-as-a-plugin.md new file mode 100644 index 00000000..b44d9248 --- /dev/null +++ b/todo/backburner/m3e-canvas-as-a-plugin.md @@ -0,0 +1,126 @@ +--- +id: backburner/m3e-canvas-as-a-plugin +title: "M3E Canvas as a plugin — sketch a UI, hand the prompt to the agent" +theme: backburner +status: open +created: 2026-09-04 +date_source: stated +order: 4 +--- + +**M3E Canvas as a plugin.** [github.com/lnkiai/m3e-canvas](https://github.com/lnkiai/m3e-canvas) — a +browser design tool for Material 3 Expressive screens that exports the design as a natural-language +prompt for an AI coding tool. MIT. + +Not decided. This card holds the measurements so the decision can be made from numbers rather than from +a README. Everything below was read from the code at **`cfde698`, 2026-09-04**. + +## Why it is interesting + +Its output is *a prompt you paste into Claude*. Officer is the thing that removes the paste — the export +button could open a chat session with the prompt already in it, pointed at a project directory on this +machine. **That last step is the only part Officer uniquely adds, and it is the whole reason to bother.** +The canvas already works standalone today. + +## What it actually is + +| | | +| --- | --- | +| size | 65 files, **14,822 lines** of TS/TSX | +| deps | `next` 16.3.4, `react` 19.2.8, `motion` ^13.1.1, `html-to-image` ^1.11.13 | +| licence | MIT | +| storage | `localStorage` only, no backend | + +**Next.js is not actually there.** The entire codebase imports it **once**, and it is a type-only import: +`import type { Metadata, Viewport } from "next"` in `app/layout.tsx`. Eighteen files carry `'use client'`. +So it is a client-side React SPA that happens to be scaffolded in Next — there is nothing to strip, and +porting means giving it a different mount point. + +That is the finding that changes the estimate. An earlier read of the README concluded "weeks of work, +own a fork forever", and that was wrong. + +## The fork line falls on a file boundary + +`app/page.tsx` imports about **twenty modules, every one a named export** through the `@/` alias, and +reaches into no internals. `lib/tokens.ts` is the shared spine with 16 importers. `app/` holds exactly +one file that matters. + +| | lines | | +| --- | --- | --- | +| `app/page.tsx` | 3,433 | the whole editor: 15 components, 103 hook calls. **Yours.** | +| `lib/ai.ts` | ~200 | **Delete it** — stores provider API keys in `localStorage`. Officer has a secret store and the agent. | +| `lib/tokens.ts` | 1,614 | Material 3 tokens. Data. Vendor. | +| `components/M3Node.tsx` | 1,233 | the component renderer. Vendor. | +| `components/Inspector.tsx` | 1,171 | Vendor. | +| `lib/prompt.ts` | 1,151 | the export engine. Vendor. | +| `components/Preview.tsx` | 899 | Vendor. | +| `components/ui.tsx` | 882 | Vendor. | +| rest | ~4,200 | `Mobile`, `i18n`, `tidy`, `Toolbar`, `color`, `shapes`, `theme`, `project`. Vendor. | + +**~3,600 lines owned, ~11,200 vendored** — and the split means something. What is vendored is *"what a +Material 3 component looks like and how to describe it in words"*, where upstream's value accumulates and +we have no wish to innovate. What is owned is *"where does this live in the workspace, whose is it, and +what happens on export"*, where upstream can never help because it does not know Officer exists. + +**So deep integration and tracking upstream are not in tension.** The integration work lives entirely in +the one file that was always going to be rewritten. + +## The export point is clean + +```ts +buildPrompt(doc: Doc, widths: Record, onlyFrameId?: string, lang: Lang): string +effectivePrompt(doc, widths, lang): string +``` + +Two pure functions in `lib/prompt.ts`. `Doc` in, string out, no side effects. No `postMessage` bridge and +no clipboard interception needed — **call the function.** Today it ends in +`navigator.clipboard.writeText` in three places; that is what gets replaced. + +`Doc` is plain serialisable JSON (`lib/project.ts` already downloads and reloads it), so persisting to a +plugin table instead of `localStorage` is nearly free. Current keys: `DOC_KEY` and `UI_KEY` in +`page.tsx`, `m3e:ai`, and `m3e:sec:`. + +## The upstream problem, measured + +This is the part that makes it a backburner card rather than a build. + +- **37 commits, 3 days old** — first 2026-09-02, and **22 of them landed on 2026-09-04**, the day this + was read. +- **Two human authors** (`lnkiai` 25, `CQMHV` 10) plus dependabot. + +It is under heavy active development *right now*. The export surface will churn, and no amount of +discipline on our side changes that. "Vendor it and track upstream" assumes an upstream that has settled, +and this one has not started. + +### If it is vendored anyway + +`web/vendor/m3e/`, committed with upstream URL, commit SHA and date in a README beside it. **The +precedent is already in the tree** — `plugins/jellyfin/web/vendor/` and `public/vendor/pako/`. + +The piece that makes it survivable is **an adapter**: Officer's panels import from one `adapter.ts` that +re-exports from `vendor/`, never from `vendor/` directly. An upstream rename then costs one file instead +of twenty. Updating is: re-copy, read the diff, fix the seam. + +## Do this first + +**Open an issue asking upstream to make the seam explicit.** If `page.tsx` grew an entry point taking the +persistence layer and the export sink — `` — we would **depend rather +than fork**, and the 3,600 owned lines shrink to a few hundred. + +Two authors, three days in, MIT, actively committing, and visibly careful about structure. That is the +most receptive an upstream ever gets, and it costs one issue to find out. **The answer changes which +version of this gets built**, so it is the cheapest thing that can happen next. + +## Open questions + +1. **Is this a design tool that lives in Officer, or the front end of "sketch it and the agent builds + it"?** If the second, `page.tsx` is where we deliberately diverge, and adopting the fork is the right + frame from day one rather than something discovered in six months. +2. **Whose designs are they?** `Doc` is per-user data. Multi-user makes that a permissions question, and + the plugin table answers it in a way `localStorage` cannot. +3. **Does the phone editor survive the port?** `components/Mobile.tsx` (534 lines) is a separate + phone-optimised editor. Officer's mobile story is a native app, so this may be redundant or may be the + best part. +4. **An iframe was considered and rejected.** It buys a day and costs the function call, the theming, the + persistence, and forces back the clipboard round-trip the whole idea exists to remove. Recorded so it + is not re-proposed as the cheap option.