Files
m3e-canvas/web/adapter.ts
T
pastilhasandClaude Fable 5 a0349cc7a3 the officer shell: designs per user, and the handoff desk
manifest, schema, router and two panels. the editor is hosted and
saving; the last step is not finished and the readme says so.

designs are rows scoped per user, with userId in every WHERE rather
than checked after the read — another person's design is 'not found',
so the id space cannot be probed. the Doc is stored whole as jsonb
because upstream adds fields to it constantly and none of them should
be a migration here.

the handoff is the reason the plugin exists and it is the part that
does not work: /api/chat has NO session-creation route, a session is
constructed by the browser opening a websocket, so a plugin has nothing
to POST to. onExport returns false and the clipboard still runs, which
is upstream's own behaviour — degraded, not broken. docs/handoff.md
names the one platform change that would finish it, and argues it
should be generic rather than ours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 12:16:35 +01:00

59 lines
2.8 KiB
TypeScript

/**
* The seam between Officer and the vendored editor.
*
* **Every import of vendored code goes through this file.** Nothing else in the plugin may reach into
* `web/vendor/`, and nothing inside `web/vendor/` may know Officer exists.
*
* That rule is the entire maintenance strategy. Upstream is days old and committing constantly —
* between two commits one day apart it renamed an import, re-indented a 3,000-line component and added
* a new guard. When it renames `Inspector` or changes `buildPrompt`'s signature, the repair is this one
* file rather than every panel. `scripts/vendor.sh` re-copies, `git diff` shows what really changed,
* and any breakage surfaces here.
*
* Upstream's maintainer reviewed this adapter's shape on lnkiai/m3e-canvas#56, called it sound, and
* declined to carry it for now. So it is ours, and it is carried where it costs least.
*/
import type { Doc } from './vendor/lib/tokens';
/** The editor itself — upstream's `app/page.tsx`, vendored as `Canvas.tsx`. */
export { default as Canvas } from './vendor/Canvas';
/**
* The export engine: two pure functions, a `Doc` in and a prompt string out.
*
* This is the reason the plugin exists. Officer's entire addition is what happens to that string —
* upstream writes it to the clipboard, we hand it to an agent.
*/
export { buildPrompt, effectivePrompt } from './vendor/lib/prompt';
/** A document is plain serialisable JSON, which is what makes server-side persistence free. */
export type { Doc, Frame, Group, Item, Palette, Theme } from './vendor/lib/tokens';
export type { Lang } from './vendor/lib/i18n';
export { isLang, setGlobalLang } from './vendor/lib/i18n';
/**
* The host hooks, wired into the vendored editor by `scripts/apply-host.py`.
*
* One correction from upstream's review is load-bearing and worth repeating at the point of use:
* **branch on whether a hook was supplied, never on what it returned.** A host returning null means
* "this user has no saved document", which must start empty — not fall back to whatever the standalone
* app left in this browser and then save that into the user's account.
*/
export { HostProvider, HostContext, useHost } from './vendor/lib/host';
export type { HostAdapter } from './vendor/lib/host';
/** Upstream's stylesheet, imported once by the panel so the editor is drawn in its own vocabulary. */
export const VENDOR_STYLESHEET = './vendor/globals.css';
/**
* The title Officer shows in a document list.
*
* Officer does not otherwise read inside a `Doc` — the schema stores it whole precisely so upstream can
* add fields without a migration here. This one reach-in is the exception, and it is in the adapter so
* it is the only place that has to change if `title` moves.
*/
export const titleOf = (doc: Partial<Doc> | null | undefined): string =>
(doc && typeof doc.title === 'string' && doc.title.trim()) || 'Untitled';