@divlet/core 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/AGENTS.md +77 -0
  2. package/BACKLOG.md +86 -0
  3. package/CHANGELOG.md +39 -0
  4. package/LICENSE +21 -0
  5. package/README.md +172 -2
  6. package/dist/core/index.d.ts +3 -0
  7. package/dist/core/index.js +4 -0
  8. package/dist/core/lib/asset-reference.d.ts +21 -0
  9. package/dist/core/lib/asset-reference.js +91 -0
  10. package/dist/core/lib/assets.d.ts +30 -0
  11. package/dist/core/lib/assets.js +125 -0
  12. package/dist/core/lib/boards.d.ts +42 -0
  13. package/dist/core/lib/boards.js +159 -0
  14. package/dist/core/lib/bridge.d.ts +5 -0
  15. package/dist/core/lib/bridge.js +1021 -0
  16. package/dist/core/lib/browser-styles.d.ts +2 -0
  17. package/dist/core/lib/browser-styles.js +3 -0
  18. package/dist/core/lib/capture.d.ts +8 -0
  19. package/dist/core/lib/capture.js +99 -0
  20. package/dist/core/lib/codec.d.ts +63 -0
  21. package/dist/core/lib/codec.js +610 -0
  22. package/dist/core/lib/contract.d.ts +202 -0
  23. package/dist/core/lib/contract.js +554 -0
  24. package/dist/core/lib/css-literals.d.ts +2 -0
  25. package/dist/core/lib/css-literals.js +11 -0
  26. package/dist/core/lib/declarations.d.ts +6 -0
  27. package/dist/core/lib/declarations.js +39 -0
  28. package/dist/core/lib/diff.d.ts +34 -0
  29. package/dist/core/lib/diff.js +102 -0
  30. package/dist/core/lib/engine.d.ts +116 -0
  31. package/dist/core/lib/engine.js +71 -0
  32. package/dist/core/lib/font-css.d.ts +10 -0
  33. package/dist/core/lib/font-css.js +151 -0
  34. package/dist/core/lib/font-woff.d.ts +5 -0
  35. package/dist/core/lib/font-woff.js +91 -0
  36. package/dist/core/lib/font-woff2.d.ts +5 -0
  37. package/dist/core/lib/font-woff2.js +111 -0
  38. package/dist/core/lib/font.d.ts +16 -0
  39. package/dist/core/lib/font.js +164 -0
  40. package/dist/core/lib/history.d.ts +45 -0
  41. package/dist/core/lib/history.js +73 -0
  42. package/dist/core/lib/html-import.d.ts +34 -0
  43. package/dist/core/lib/html-import.js +119 -0
  44. package/dist/core/lib/hub.d.ts +18 -0
  45. package/dist/core/lib/hub.js +269 -0
  46. package/dist/core/lib/import.d.ts +52 -0
  47. package/dist/core/lib/import.js +191 -0
  48. package/dist/core/lib/launch.d.ts +60 -0
  49. package/dist/core/lib/launch.js +55 -0
  50. package/dist/core/lib/media-type.d.ts +8 -0
  51. package/dist/core/lib/media-type.js +20 -0
  52. package/dist/core/lib/model-styles.d.ts +1 -0
  53. package/dist/core/lib/model-styles.js +2 -0
  54. package/dist/core/lib/model.d.ts +167 -0
  55. package/dist/core/lib/model.js +453 -0
  56. package/dist/core/lib/ops.d.ts +99 -0
  57. package/dist/core/lib/ops.js +482 -0
  58. package/dist/core/lib/output.d.ts +39 -0
  59. package/dist/core/lib/output.js +266 -0
  60. package/dist/core/lib/plugins.d.ts +95 -0
  61. package/dist/core/lib/plugins.js +85 -0
  62. package/dist/core/lib/project.d.ts +82 -0
  63. package/dist/core/lib/project.js +331 -0
  64. package/dist/core/lib/projects.d.ts +66 -0
  65. package/dist/core/lib/projects.js +247 -0
  66. package/dist/core/lib/public-snapshot.d.ts +42 -0
  67. package/dist/core/lib/public-snapshot.js +54 -0
  68. package/dist/core/lib/render.d.ts +27 -0
  69. package/dist/core/lib/render.js +62 -0
  70. package/dist/core/lib/runtime.d.ts +842 -0
  71. package/dist/core/lib/runtime.js +1951 -0
  72. package/dist/core/lib/server.d.ts +78 -0
  73. package/dist/core/lib/server.js +344 -0
  74. package/dist/core/lib/site.d.ts +63 -0
  75. package/dist/core/lib/site.js +407 -0
  76. package/dist/core/lib/static.d.ts +44 -0
  77. package/dist/core/lib/static.js +858 -0
  78. package/dist/core/lib/svg-resource.d.ts +6 -0
  79. package/dist/core/lib/svg-resource.js +86 -0
  80. package/dist/core/lib/validation.d.ts +16 -0
  81. package/dist/core/lib/validation.js +67 -0
  82. package/dist/plugins/pen/index.d.ts +29 -0
  83. package/dist/plugins/pen/index.js +54 -0
  84. package/dist/plugins/pen/lib/convert.d.ts +23 -0
  85. package/dist/plugins/pen/lib/convert.js +298 -0
  86. package/dist/plugins/pen/lib/pen-mesh.d.ts +19 -0
  87. package/dist/plugins/pen/lib/pen-mesh.js +129 -0
  88. package/dist/plugins/pen/lib/pen-paint.d.ts +6 -0
  89. package/dist/plugins/pen/lib/pen-paint.js +47 -0
  90. package/dist/plugins/pen/lib/pen-path.d.ts +2 -0
  91. package/dist/plugins/pen/lib/pen-path.js +28 -0
  92. package/dist/plugins/pen/lib/pen-polygon.d.ts +2 -0
  93. package/dist/plugins/pen/lib/pen-polygon.js +25 -0
  94. package/dist/plugins/pen/lib/projection.d.ts +7 -0
  95. package/dist/plugins/pen/lib/projection.js +450 -0
  96. package/dist/plugins/pen/lib/source.d.ts +48 -0
  97. package/dist/plugins/pen/lib/source.js +531 -0
  98. package/dist/plugins/pen/lib/validate.d.ts +15 -0
  99. package/dist/plugins/pen/lib/validate.js +195 -0
  100. package/dist/plugins/tailwind/index.d.ts +31 -0
  101. package/dist/plugins/tailwind/index.js +14 -0
  102. package/dist/plugins/tailwind/lib/identity.d.ts +2 -0
  103. package/dist/plugins/tailwind/lib/identity.js +50 -0
  104. package/dist/plugins/tailwind/lib/renderer.d.ts +8 -0
  105. package/dist/plugins/tailwind/lib/renderer.js +17 -0
  106. package/dist/plugins/tailwind/lib/styles.d.ts +12 -0
  107. package/dist/plugins/tailwind/lib/styles.js +180 -0
  108. package/dist/plugins/tailwind/lib/styling.d.ts +46 -0
  109. package/dist/plugins/tailwind/lib/styling.js +497 -0
  110. package/dist/plugins/tailwind/web/classes.d.ts +5 -0
  111. package/dist/plugins/tailwind/web/classes.js +167 -0
  112. package/dist/plugins/tailwind/web/layout.d.ts +13 -0
  113. package/dist/plugins/tailwind/web/layout.js +632 -0
  114. package/dist/plugins/tailwind/web/panel.d.ts +1 -0
  115. package/dist/plugins/tailwind/web/panel.js +15 -0
  116. package/dist/plugins/tailwind/web/tw.d.ts +71 -0
  117. package/dist/plugins/tailwind/web/tw.js +259 -0
  118. package/dist/skills/divlet/SKILL.md +236 -0
  119. package/docs/README.md +30 -0
  120. package/docs/agents.md +124 -0
  121. package/docs/architecture.md +188 -0
  122. package/docs/board.md +162 -0
  123. package/docs/browser-runtime.md +107 -0
  124. package/docs/format.md +345 -0
  125. package/docs/publishing.md +65 -0
  126. package/docs/site.md +140 -0
  127. package/docs/testing.md +105 -0
  128. package/docs/web-native.md +93 -0
  129. package/examples/README.md +31 -0
  130. package/examples/handbook/content/guide/assets/shared-layout.svg +17 -0
  131. package/examples/handbook/content/guide/index.md +26 -0
  132. package/examples/handbook/content/guide/start.md +34 -0
  133. package/examples/handbook/content/index.md +23 -0
  134. package/examples/handbook/project.divlet +559 -0
  135. package/examples/handbook/site.json +11 -0
  136. package/examples/landing/landing.divlet +2561 -0
  137. package/examples/studio/project.divlet +1314 -0
  138. package/package.json +160 -4
  139. package/skills/divlet/SKILL.md +236 -0
package/AGENTS.md ADDED
@@ -0,0 +1,77 @@
1
+ # divlet project context
2
+
3
+ divlet is a local browser layout board for agents. It edits validated static HTML/SVG/CSS projects, not arbitrary applications. Start with [README.md](README.md) for usage and [docs/README.md](docs/README.md) for technical documentation. The [web-native core contract](docs/web-native.md) owns the target semantics and plugin boundaries; [docs/format.md](docs/format.md) owns the wire-format specification and its explicit compatibility status.
4
+
5
+ ## Meta-Protocol Principles
6
+
7
+ - **Constraint-driven structure:** Add context or abstraction only when it preserves a real constraint or makes an existing responsibility easier to find. Consolidate stale context rather than accumulating parallel instructions.
8
+ - **Durable rules:** This file owns contributor constraints and recurring engineering guidance, not task status or delivery history.
9
+ - **Open work:** [BACKLOG.md](BACKLOG.md) is the only task backlog. Keep only remaining, blocked or gated work there.
10
+ - **Completed changes:** [CHANGELOG.md](CHANGELOG.md) records implemented outcomes. Unreleased work must not be presented as a published release.
11
+ - **Human documentation:** README files are entrypoints; `docs/` owns architecture, operational guidance and the format reference ([docs/format.md](docs/format.md)).
12
+ - **Reconciliation:** When behavior or ownership changes, update its authoritative document, reconcile affected backlog items and record meaningful delivery. Do not duplicate the same status across files.
13
+ - **Git:** The repository is local. Commit after each coherent step.
14
+
15
+ ## Topology
16
+
17
+ - **`core/` and `web/` are peers** (plugins can act on either). `core/` holds the server-side core (`core/index.js` entry, `core/lib/`, `core/tests/`); `web/` is the web client (`board.js` entry, `index.html`, `board.css`; its modules, one per domain, in `web/lib/`); `plugins/` and the real adapter packages `cli/`, `mcp/` and `pi/` sit beside them; do not add a grouping/wrapper directory.
18
+ - **Core:** Own one native model, its format/admission/projection and the local editor runtime. HTML admission is distinct from strict native decoding and managed-cache reconciliation; the browser owns layout and painting. Use the [current module map](docs/architecture.md#modules) for source locations and the [web-native contract](docs/web-native.md) for target semantics, not interchangeable plugin-defined document models.
19
+ - **Plugins:** Foreign formats use importer/exporter packages under `plugins/<id>/`; native HTML admission remains core-owned, bounded and non-overwriting. PEN interpretation must yield ordinary HTML/SVG/CSS without requiring a style engine. Tailwind owns its declaration/compiler and inspector capabilities, not the native model. Generic core layers depend on contracts, never concrete providers; select providers in composition code. Each plugin owns its dependencies in `package.json`, with a thin `index.js`, internals in `lib/` and tests in `tests/`. Client modules are served at `plugins/<id>/<file>` and import core client modules as `../../lib/<file>`. See the module map for current owners and the backlog for remaining composition gaps.
20
+ - **Adapters:** The real host packages in `cli/`, `mcp/` and `pi/` publish as `@divlet/cli`, `@divlet/mcp` and `@divlet/pi`; each owns its implementation and tests, with no wrapper layer. They consume public `@divlet/core` and `@divlet/core/contract`, never core internals or the reverse dependency; use the same entry/internal/test shape as plugins and start no service at import time. They translate the shared capability contract rather than re-implement operations. Their versions match the root product release and their `@divlet/core` dependency is exact. The dependency-free `@divlet/web` peer owns browser assets and portable host ports; only core's HTTP composition resolves its public asset locator. Core pins its exact release version; web never imports core, including through JSDoc. The root library stays harness-agnostic. The portable workflow in `skills/divlet/` belongs to the product, not a host.
21
+ - **Agent workflow:** `skills/divlet/` belongs to the product, not to an adapter: it is the portable workflow an agent loads (Pi finds it through the extension; other hosts install it globally). It is the product's **entry point**, written for an agent that has nothing else: install the tools (an MCP host registers `npx -y @divlet/mcp`), open a project, then follow the loop. Keep it a walkthrough, not a reference dump — every claim it makes must be true of the code, and `core/tests/skill.test.js` enforces exactly that (tool names, CLI flags, error codes, documents, front-matter limits). It is distinct from this contributor guide.
22
+ - **Tests:** `core/tests/` owns server/model unit tests; `web/tests/` owns frontend unit and browser acceptance suites. Frontend tests are neither served client modules nor publishable assets; build copying and module-graph checks must exclude them. Shared core fixture builders stay in `core/tests/fixtures/`. The Pi RPC scenario and its mock provider live with their adapter in `pi/tests/e2e/pi.mjs`, plugin tests in `plugins/<id>/tests/`. See [testing](docs/testing.md). Generated screenshots belong in the ignored `artifacts/` directory.
23
+
24
+ ## Operating Principles
25
+
26
+ - **Language and identity:** Write project documentation and code comments in English. Keep the public identity `divlet`, the `/divlet` command and `*_divlet` tools. Tool contracts use `frames` and `frameId`; internal code may use `document` and `documentId`.
27
+ - **Format direction:** Follow the [web-native core contract](docs/web-native.md): HTML/SVG/CSS is native; Tailwind is an optional representation/compiler/UI plugin, even when enabled by default. Class names are ordinary HTML classes and tokens are CSS variables. Plain-CSS creation, editing and rendering must not require a style engine. Keep structural fields explicit and metadata extensible within the safe JSON profile; shared semantics should have one validator rather than context-specific copies. This is the target direction, not a claim that every current path already conforms; remaining implementation work belongs in [BACKLOG.md](BACKLOG.md).
28
+ - **Native HTML, plugins for foreign formats:** External HTML admission belongs in the core and stays distinct from strict native decoding, reverse-cache reconciliation and clean export. A foreign format is an importer plugin. Never name a foreign format in the core, and never edit a foreign source in place: it is converted once into a sibling `.divlet` (an existing `.divlet` is never overwritten, an unconvertible source opens as itself with diagnostics). Imported bytes are never rewritten; the one exception is a legacy `.divlet` that still holds a foreign source — its bytes are moved to a free `<name>.pen` and the `.divlet` path becomes the converted project.
29
+ - **Initial schema marker:** Native project and standalone-frame envelopes remain `schemaVersion: 1` until the first format freeze. Unpublished draft iterations do not establish schema generations 2/3; do not introduce another draft-version discriminator or numbering-only migration instead. Keep npm versions and content revisions separate, preserve strict validation and never rewrite opaque metadata or foreign-format versions as part of numbering.
30
+ - **Source ownership:** Each `<name>.divlet` file is canonical and independently identified by its full path. Default creation is `project.divlet` in the chosen directory, without a wrapper folder. Preserve hand edits rather than silently overwriting them.
31
+ - **Editor state is not content:** Selection, hover, tools, viewport navigation, panel preferences and measured element geometry address the model but do not become project content by default or advance its content revision. Authored artboard geometry is content. Keep internal IDs, DOM IDs and instance occurrence addresses distinct. User-set panel widths are primary local preferences: defaults/toolbar following apply only without a valid preference; viewport/scale constraints must never overwrite it and cancelled resizing must not commit. Reset is explicit. Browser storage is origin-scoped (including the port), not cross-launch storage by itself.
32
+ - **Managed projection:** The target HTML/CSS cache is a rebuildable, reverse-editable projection, not a second source of truth. Reverse edits must preserve identity, component meaning, resource provenance and authored-versus-generated CSS, and reject stale or ambiguous candidates through the same acceptance path as tools and JSON edits. Clean static export is a separate contract; do not claim the editing round trip is implemented merely because HTML can be decoded.
33
+ - **Style transformations:** Core style edits must have a plain-CSS path. Optional normalization must be explicit, idempotent and cascade-preserving, reconciling superseded inline declarations and utilities. Retain ordinary CSS when equivalence is unproven. Missing required plugins must be diagnosed; disabling a dependent engine must preserve styling through a validated conversion or refuse, never silently drop compiled CSS.
34
+ - **Output ownership:** `core/lib/output.ts` owns generated-file provenance, collision checks and symbolic-link refusal. Never overwrite unowned output paths or share one output directory between projects. Generated files belong in `.divlet/<name>/` (or the project's relative `outputDir`). Keep the public static graph distinct from the complete disk-publication/repair plan: editor sidecars must remain repairable without becoming HTTP-served public files.
35
+ - **Local filesystem model:** Projects may live and be edited anywhere permitted by the OS; referenced local resources resolve automatically, including absolute paths. Keep bounded regular-file reads, no-link checks and non-overwriting publication. This is trusted local-project access, not an arbitrary HTTP filesystem route.
36
+ - **Portable semantic slices:** Browser extraction shares admission/operation code with Node rather than duplicating validators or hiding host dependencies behind blanket polyfills. Keep Node built-ins and concrete provider bootstrap outside a declared portable slice; preserve existing standalone provider defaults and the connected host's writer authority. Prove browser operations with real browser imports, not TypeScript compilation alone. See [browser runtime boundaries](docs/browser-runtime.md).
37
+ - **Validated commits:** Check the expected version, prepare and validate the candidate before changing accepted state, and retain the last working state on invalid external edits. Do not bypass these rules in HTTP handlers or Pi tools.
38
+ - **History safety:** Undo/redo must validate a whole group or requested multi-step traversal before committing it and refuse overlapping later edits without changing state. Move stack authority only after the full candidate persists; never implement history navigation as a browser loop of independently accepted Undo calls. Frame order is authored state: lifecycle replay restores original slots, and reorder replay checks its expected relative order without rearranging unrelated later frames. State-point IDs name proven stack endpoints, not audit sequence numbers: invalidate extended group frontiers and expose nonlinear/untracked boundaries without destroying legacy Undo/Redo. Check a navigation request's expected point inside the writer queue and advance point authority with the committed stacks.
39
+ - **Preview trust:** A matching iframe `WindowProxy` is not authentication after navigation. Keep model, selection and editing traffic on the authenticated document-bound MessagePort; keep referrer protection and capability rotation on preview reset, and retire the editor port/capability before entering a separate read-only browsing document.
40
+ - **Serialized bridge code:** Editor, site and read-only browsing helpers injected through `Function#toString` must stay free of imports and outer-scope dependencies.
41
+ - **Local HTTP boundary:** Preserve loopback binding, token-scoped routes, Host/Origin checks, CSP, request-size bounds and filesystem confinement.
42
+ - **One writer per project:** A board owns its project file and is the only writer. Clients (browser, Pi, MCP, CLI) attach to the board found in `core/lib/boards.ts`; do not open a second runtime for a project another live board owns. Attached traffic uses the board's token-scoped `api/tool` route, which runs the same contract.
43
+ - **Long-lived frames:** Each artboard keeps one iframe for the project's lifetime, loaded lazily by the browser (`loading="lazy"`, eager only for selected/expanded frames). Do not reintroduce scroll/zoom-driven unmounting; swap a frame only behind a ghost of its previous page. At far zoom a cached PNG may stand in for the live page.
44
+ - **TypeScript:** The product is TypeScript. Migrated sources are `.ts`. The browser remains native JavaScript ES modules pending its typing strategy; current `tsconfig.json` excludes `web/` and sets `checkJs: false`, so the TypeScript gate is not evidence of full JavaScript/client checking. Node runs the sources directly by stripping types — there is no build in the development loop — so a file's own imports name the real file (`./model.ts`), the compiler is configured `strict` with `erasableSyntaxOnly` (no enums, namespaces, parameter properties, decorators), and `npm test` starts with `npm run typecheck` (strict Node/core plus the scoped DOM-only client profile); `tsconfig.web.json` owns the checked browser transport subset, public web ports/asset locator and their type regression, not a full-client checking claim. `dist/` is generated by `npm run build` for publishing only: never edit it, never commit it. Vocabulary belongs in `core/lib/model.ts`; a module types its own boundary and treats foreign input as `unknown` until the validator accepts it.
45
+ - **Embedded-resource target:** Project-owned file-asset bytes belong in the native `.divlet`, with only disposable materialization under `.divlet/<project>/assets/` beside generated pages. A canonical sibling `assets/` folder or a cache-only copy is not the target. The declared hierarchical `assets` tree owns reusable names/paths; explicit references materialize those files, while literal Data URLs at use sites remain inline. Never automatically extract inline URLs or rename declared keys merely to deduplicate bytes. Implement storage/reference validation, byte budgets and all resource consumers coherently before claiming this capability; preserve existing resources until conversion retains their bytes. Preserve the existing bounded local-resource import model; resource identifiers never authorize executable content or unrequested network fetches.
46
+ - **Public demo data:** Never use `runtime.info()` as a public snapshot template: it includes private paths and session/editor state. Project inspection data only from explicitly curated public prepared content, with an allowlisted state shape and deliberate presentation geometry. Keep prepared HTML as rendering authority; metadata omission/model validity do not establish provenance, content secrecy, reference closure or whole-UI read-only enforcement.
47
+ - **One authored landing:** Each example owns its folder. The canonical English onboarding/demo source belongs at `examples/landing/landing.divlet`; its `index` frame generates `examples/landing/.divlet/landing/index.html`. Keep identity, commands and release status consistent with the root README. Do not maintain a second hand-authored HTML site or marketing tree in core code. Other bundled examples may remain importer/site/layout scenarios, not duplicate landing sources. Any composition that seeds workspaces from this template must create an independently owned validated copy, never edit the installed master; explicit paths and no-demo creation remain authoritative.
48
+ - **The landing build is derived, tracked and selectively published:** the named `examples/landing/.divlet/landing/` build is an explicit exception to ignoring generated project output, not permission to hand-edit it or commit arbitrary caches. Regenerate through normal validation and output ownership; a fresh checkout must rebuild safely, with portable provenance where required. Never track locks, recovery trash or session capabilities. CI publishes the prepared portable HTML/CSS/resource set to the `gh-pages` root, not a raw cache-directory copy: sidecars, ownership metadata, trash and editor/private payloads are not public artifacts. `scripts/landing-hash.mjs` owns both the published file set and its fingerprint; unchanged public bytes mean no deployment commit. Check source/build freshness in a relocated sandbox, preserve self-contained rendering and relative URLs, and do not mistake this architecture for authorization to push or deploy. Keep the canonical source/assets in the npm example payload, but exclude this cache; the Pages public set adds only its required `.nojekyll` marker and fingerprint record, not editor metadata.
49
+ - **The inspector is one vertical flow:** Section summaries are the only section-navigation controls; do not duplicate them in horizontal tabs, floating jump bars or reorder strips. Keep sections in their owning panels' stable order and remember open/closed state as a local preference, not project content. Text alignment belongs beside text colour, size and weight in Appearance; Layout owns container and item alignment. The read-only measured X/Y/W/H and overflow block is the final section, after metadata. Use native keyboard-accessible disclosure controls, unobstructed vertical scrolling and whole label/control units that reflow at narrow widths; minimal chrome must not remove access to properties.
50
+ - **A block header names the block:** A section's caption says what the section is ("Artboards", "Elements"), not which item is selected — the selection is already lit in the list or tree it belongs to, and repeating its name costs the width a narrow panel needs. Detail about the current choice belongs on the row, in a tooltip, or in the empty state, never in the header.
51
+ - **One slot, one occupant:** When a host can do something another cannot, the control replaces the affordance it stands in for rather than stacking beside it — the composer's footer is either the key hints (a host that can only answer prompts) or the model/thinking selectors (a host that owns the session), never both. Pi users are assumed to know the shortcuts; the selectors carry their tips, and `?` still lists every key. Send remains the footer's bottom-right action when either occupant wraps: use its own auto margin and bottom alignment, not a preceding flex spacer.
52
+ - **Entry points say what they are:** The root `@divlet/core` package's entry is the product library, with named public subpaths such as `./runtime`, `./contract`, `./model`, `./boards`, `./plugins`, `./engine` and the shared `./skill` resource. It has no bins, Pi manifest/peer requirements or `./pi` export. Each adapter package owns its `dist/index.js` entry, exports and relevant bin or `pi.extensions`; host-specific keywords and dependencies stay there. A folder-scanning Pi installation targets the built `pi` package, never the repository root. Source execution uses the `divlet-source` export condition; `scripts/source.mjs` propagates it through `NODE_OPTIONS` to children. The npm workspace includes `.`, `web`, `cli`, `mcp`, `pi` and `plugins/*`. The root remains the `@divlet/core` library manifest, not a private wrapper. `web/package.json` owns `@divlet/web` directly; its DOM-only build compiles public ports and copies native browser assets only to `web/dist/`, never into the core tarball. Development runs sources; `dist/` is publishing output only.
53
+ - **Host capabilities are advertised, not assumed:** anything only one host can do (the Pi extension can change the session's model and thinking level; an MCP client cannot) is published as data in the board state (`agent.controls`), turned into a control when present and **absent** when not — never a disabled control, never a guessed value. The board stores no session setting as project content, asks only for values the host listed, and shows what the host reported applying. The host is the authority; the board is a view.
54
+ - **Scope discipline:** Preserve unrelated work. Do not publish, tag or deploy without explicit authorization.
55
+
56
+ ## Interface rules
57
+
58
+ - **Chrome layout:** Islands stay in place: DIVLET button and search (top left, separate pills), the three tabs (bottom left, separate pills that join the sidebar when it opens) and the toolbar (bottom right) with the inspector attached above it. The sidebar (Layers / History of changes / Agent conversation) is one homogeneous surface: no horizontal border dividers, nested darker areas instead. Breadcrumbs sit bottom centre only (wrapping when narrow; sidebars cover them on phones). A single selected artboard folds the path to an accessible root-element icon; activating it selects the actual root and opens the path, while the artboard crumb folds it again. Keep keyboard focus in that path across folding/unfolding and refuse late root reads after a newer navigation. Select-root belongs here, not the artboard/inspector action menus; Rename is likewise absent from those menus, with direct name editing, R and palette access retained. A tab both switches and, when already open, folds the sidebar. Tab density follows the tab row's own width, not pane visibility: icons always stay to the left of complete text labels, never stacked above them. Counters may wrap within the text column when needed; folding must not reset that layout or change the tab row height. The composer's context header has intrinsic height and a real gap before the input, including when it wraps.
59
+ - **One filesystem browser:** `web/lib/file-picker.js` owns the reusable path field, parent navigation, listing rows, keyboard navigation and latest-read/disposal guards. Open/copy and Settings compose it instead of maintaining separate browsers. Inject the read-only listing; keep confirmation, relative-output conversion, project writes and modal lifetime in the owning workflow. Reuse it for equivalent filesystem choices, not for resource galleries with different admission semantics.
60
+ - **Two homes, at most, for an action:** Keep one natural control location plus the general indexes (shortcuts, command palette/search, agent tools), never duplicated interface controls. Before adding one, name its homes and search for existing affordances; move or merge rather than copy. Closing the left sidebar is the active tab (or `L`/`H`), not a separate panel/corner close button.
61
+ - **UX stability:** Errors, statuses and progress never move controls: messages render after the controls they concern, progress is drawn on the element that is working, action buttons keep a stable size. The Agent activity plaque is a five-column, three-row grid: indicator at the start, activity text across the upper rows, time in the footer and Stop in the bottom-right cell; do not squeeze them back into one flex row. While busy and the entire left sidebar is folded, move that same plaque above the tabs at the remembered sidebar width, never clone it or add a second Stop. In this floating form, put the spinner in the bottom-left cell, vertically centred with the timer and Stop in the footer, and let the description span the full width above; the open-panel indicator stays in its original upper cell. Tabs stay put; long floating activity scrolls inside its bounded text area, and phone sheets clear the measured footer. Idle and expanded browsing show no floating plaque.
62
+ - **Chat stacks by the composer:** Keep chronological DOM order and bottom-align spare space with an auto margin, never a reversed column or overflowing `flex-end`. Only the conversation scrolls once full. Follow arrivals and layout resizing while near the bottom; preserve an older reading position and explicit message focus.
63
+ - **Fluid layout:** A label and its control are one unit and wrap together, everywhere. Values (selector choices, key caps, counts, times, tags) are as wide as their text and never wrap or squash. Numeric fields need room for their actual/default text, not merely an overflow-free wrapper around a zero-width input. Let control grids respond to their available width instead of forcing a phone column count. Match minimums to content, not one blanket floor: short bounded fields such as radius keep compact readable widths even in a narrow inspector, while selector fields move to a new row when their contents do not fit. Keep each selected value whole on one line; only its label may abbreviate, retaining its full accessible name and tooltip. Prefer compact controls; test narrow widths (195px ≈ 200% on a phone).
64
+ - **Box-model editing stays direct:** Use nested position → margin → border → padding bands with full layer names and measured content dimensions at the centre. Keep exactly eight independent margin/padding fields, native label targets and single-side highlighting; no linking modes or duplicate editors. Position mode and all four offsets live in Spacing, with offsets on the outer band even for static elements; changing an offset does not implicitly change position mode. Border is a separate vertical disclosure immediately after Spacing, owning line style, colour, radius and four edge widths. Those widths deliberately have two synchronized views — Border and their box-model band — bound to the same authored properties and writer queue, including Undo and refusal recovery; never add a third Appearance copy. Synchronizing an inactive peer must not commit another edit, replace the panel or overwrite a focused draft. The centre edits content width/height for one measured element, accounting for the current render's box sizing, padding and borders; unavailable geometry must not invent dimensions. Native Tab/Shift+Tab order follows position mode, then each band's top/right/bottom/left values from outside in, then width/height, and leaves the widget normally. Edits must compose in the document queue without replacing controls under keyboard focus. Bands are schematic; preserve signed/fractional values, use `—` for unavailable measurements and give long values room instead of clipping.
65
+ - **Smart toolbars and units:** A toolbar fits itself: its actions sit in one row and whatever does not fit moves, in order, into a trailing dropdown built from the same list (`web/lib/toolbar.js`), never a hard-coded split. Chrome text is shown whole (it wraps; no ellipsis because a toolbar reached the screen edge). Tooltip copy and headings wrap, including unbroken references; cap their width to the scaled viewport and place them using their measured multiline size. Numeric fields show their unit (px, %) beside plain numbers. Dropdown labels and values are vertically centred, with labels carrying stronger contrast than values. The inspector is as wide as the toolbar under it by default and a double-click on its edge restores that.
66
+ - **Contracts are types:** A plugin or adapter boundary is an exported type, not a paragraph: `Importer`/`Exporter` in `core/lib/plugins.ts`, `StyleEngine` in `core/lib/engine.ts`, the capability schema and agent-host `Session` in `core/lib/contract.ts`, and the board's transport-free `BoardReadSession` in `web/lib/board-session.ts`. The public plugin/agent contracts ship through `@divlet/core/plugins`, `@divlet/core/engine` and `@divlet/core/contract`; board ports ship through `@divlet/web/session` and `@divlet/web/host`, all with emitted Node-free declarations. All these contracts must stay free of Node-only types so a plugin author can compile against them; `SourceBytes` is a `Uint8Array` for that reason. A JavaScript descriptor is glued in exactly one place per registry (`registerImporter`, `registerStyleEngine`, `registerExporter`), which is where the promise that it satisfies the contract is written down.
67
+ - **Prompt blocks, one grammar:** Anything the product injects into an agent's context is one compact block, in the shape the other extensions use: a bracketed English header with `|key:value` metadata, its value on the same line, lists from the next line in Markdown, then the payload after a blank line — `[divlet|project:<id>|frames:<n>|nodes:<n>] about “Pricing” with 1 element selected`, then `frames:` / `elements in <frame>:` / `next:`, then the user's request. Build board requests in `runtime.askMessage` (one place for Pi, MCP and anything later); a host may add its own per-turn context but not invent a second prose format.
68
+ - **Layers always has an artboard:** While the Layers pane is open, one artboard is chosen — the remembered one if it still exists, else the first — so the pane never shows an empty right-hand side next to a populated list; a project without artboards is the only case with nothing chosen. Enforce it where the pane renders (never in a general selection setter), so a canvas click or an Escape that clears the selection bounces back to the artboard the user had.
69
+ - **Icons:** One painted element per icon — a single `<path>` (a `<g>` only when one transform must apply to the whole glyph, as in the gear; a second element only when it is genuinely a different paint, as the filled half of `auto`, the dashed frame of `wrap`, or the logo's outlined bracket and filled D). Never put alpha on a part: opacity, `fill-opacity` and `stroke-opacity` belong to the icon element, so a crossing, a dot over stroke ends or a touching join cannot darken. Merge touching sub-shapes into that path instead of stacking elements.
70
+
71
+ ## Execution and validation
72
+
73
+ 1. Read this file, the relevant docs and any affected entrypoint before changing code. Consult the backlog when selecting open work.
74
+ 2. Trace the affected implementation, callers and tests. Prefer the smallest change at the owning boundary and a regression for the failure class.
75
+ 3. Run the smallest decisive tests first (`node scripts/source.mjs --test …`, `npm run test:browser:all`, `npm run test:pi`); see the [testing guide](docs/testing.md). Documentation-only changes need source link checks; when shipped links change, also run `node scripts/source.mjs --test core/tests/package.test.js` for the packed documentation graph, not unrelated runtime/browser suites.
76
+ 4. Reconcile documentation and backlog state; keep completed work out of the backlog and describe unresolved limitations honestly.
77
+ 5. Commit; report changed paths, checks actually run, remaining limitations and any external gate.
package/BACKLOG.md ADDED
@@ -0,0 +1,86 @@
1
+ # divlet backlog
2
+
3
+ This is the canonical list of remaining work. Contributor rules live in [AGENTS.md](AGENTS.md); completed changes belong in [CHANGELOG.md](CHANGELOG.md). Scope boundaries documented in [README.md](README.md) are not implicit feature commitments.
4
+
5
+ Order is priority, highest first. Every item names what is left, not history.
6
+
7
+ ## P1 — format freeze preparation
8
+
9
+ Implement the [approved first-release profile](docs/format.md#approved-first-release-profile): ordered CSS declarations, native cascade, explicit renderer requirements, consumer component scope, conflict/DOM-ID refusal, finite binary64 with equivalent signed zeros, and explicit non-overwriting structural/style conversion where needed. Both native project and standalone-frame envelopes remain `schemaVersion: 1` until the first freeze; no speculative schema 2/3 or numbering-only migration is required. Approval covers implementation, not freezing or publishing. Preserve working draft semantics until their replacement passes a coherent cutover; do not silently reinterpret or overwrite draft sources. The subsequent resource decision requires asset bytes inside the native `.divlet`, with materialized assets only in its generated cache; implement FORMAT-ASSETS without changing the schema-1 marker. Preserve existing file resources and reusable image/SVG elements until a validated conversion can retain their bytes.
10
+
11
+ Checkpoint: one validated native schema-1 profile, explicit non-overwriting draft migration, accepted/rejected conformance evidence, and no hidden renderer/cascade/identity assumptions. Do not make all editor work a format-freeze prerequisite.
12
+
13
+ - [ ] **FORMAT-TRANSITION — Cut over the model and its consumers coherently.** Translate literal/override styles, validators, codecs, patching, resource rewriting, importer outputs and browser style consumers to ordered data; add the typed `requires` envelope with coherent schema-1 readers/writers. Do not advance the marker or add a substitute draft-version discriminator before the first freeze. Resolve provider semantics through WEB-3 and component semantics through WEB-4, not ad-hoc adapter exceptions. Verify native loading, source/HTML round trips, editor consumers and package declarations; never publish an intermediate source that the runtime cannot reopen.
14
+ - [ ] **FORMAT-VALUES — Enforce the approved JSON value profile at its owning boundaries.** Normalize numeric zero without rewriting opaque metadata strings, retain finite binary64/precision-string semantics, and reject escape-equivalent duplicate native JSON members before object parsing loses evidence. Introduce the rule with the coherent pre-freeze profile update, not a silent change to legacy files. Verify raw reader witnesses, retained source/state on refusal and ordered-data round trips.
15
+ - [ ] **FORMAT-MIGRATION — Admit draft sources through an explicit copy operation.** Preserve original bytes, declaration/operation order, resource bases and output isolation; require an explicit renderer selection and make legacy cascade wrappers explicit when preserving appearance. Validate the full target before exclusive sibling publication; refuse ambiguous override/ID/lexical cases rather than guessing. Verify existing destinations, linked paths, failures and successful independently reopenable copies.
16
+ - [ ] **FORMAT-ASSETS — Complete resource conversion and remaining consumer conformance.** Complete the [approved resource ownership](docs/format.md#approved-embedded-resource-ownership) through explicit legacy-input conversion, remaining SVG consumers, font controls/capture and installed/public-output acceptance below. Preserve separately declared names, MIME, suffixes and dependency bases; only explicit normalization may reuse an exact-byte-compatible key, with all references updated together. Existing files remain authoritative until conversion retains their bytes, and literal Data URLs stay inline. Do not hand-edit generated HTML, bypass a live writer, change the schema marker or add an Assets tab. Keep separate authored Markdown/site-input ownership explicit; this decision does not authorize absorbing those inputs.
17
+ - [ ] **ASSET-CONSUMERS — Complete explicit legacy resource conversion and remaining SVG consumers.** Capture existing referenced filesystem graphs through a bounded, non-overwriting conversion that retains bytes, MIME, aliases, suffixes and dependency bases; compare compatible-key reuse by exact bytes and update every explicit reference together. Preserve legacy inputs until conversion succeeds and never extract literal Data URLs implicitly. Prove faithful file-based SVG `use` in the real opaque-origin preview before lifting its native declared-resource refusal; preserve inline/same-document SVG use meanwhile. Broader SVG dependency cases need real image-context proof, not just a prepared graph. No live-owner restart, new schema marker, Assets tab or implicit network fetch.
18
+ - [ ] **ASSET-FONTS — Complete font device admission and far-zoom capture.** Keep bytes in the common Data-URL tree and ordinary CSS, never a font metadata/payload store. Normal reads stay payload-free and the family selector retains its conservative shorthand/dynamic-value refusals. No release ordering or fonts-only native format is implied.
19
+ - [ ] **FONT-FACE-MERGE — Broaden companion admission only where meaning is proven.** Resolve authored conditional/layered faces, explicit oblique-angle relationships, aliases, duplicate/dynamic/unknown descriptors and any user-directed rename through an explicit scope/identity policy before lifting their conservative refusals. Preserve unrelated rules and bytes; overlaps stay refused. Mutable site/imported stylesheet admission needs a dependency-aware candidate check, not a whole-family bypass.
20
+ - [ ] **FONT-CONTAINERS — Extend the bounded admission profile deliberately.** Extend beyond the [null-transform WOFF2 profile](docs/format.md#woff2-admission-gate) only through caller-enforced actual-output and decoder-working-memory bounds for transformed glyf/loca/hmtx data; a header-size check or worker heap limit is not that contract. Handle collection/legacy face selection separately. Resolve WOFF 1 extended metadata/private blocks and combined italic/slant-axis semantics explicitly before lifting their refusals. Preserve original bytes and names; do not introduce a second store or silently reinterpret unsupported fonts.
21
+ - [ ] **FONT-CAPTURE — Define wider stylesheet/font dependency semantics.** Imported/linked CSS and dynamic font carriers require explicit dependency/condition semantics before admission; unsupported cases must remain live rather than use fallback-font thumbnails. Authored file-URL query/fragment semantics remain refused in byte-only capture. Extend only through a proven accepted public graph, never preview fetch or new HTTP filesystem authority.
22
+ - [ ] **ASSET-ACCEPTANCE — Close installed-artifact and static-host resource acceptance.** Complete remaining installed resource/adapter coverage beyond the CLI/MCP and real-Pi font upload/render/read/history scenarios, including images/SVG and remaining host-specific independent-copy paths, with payload-free reads. Prove conventionally named resources on a prefixed static host and refusal of MIME/name/parameter incompatibility without silently omitting bytes or publishing editor sidecars. Keep compiled-public-module smoke separate from installed-artifact/browser evidence. Refresh the complete acceptance cohort after font controls and explicit legacy conversion, including original/cache deletion, dependency/suffix closure and output/fingerprint ownership.
23
+ - [ ] **FORMAT-FREEZE — Approval-gated after implementation and evidence.** Retain passing reader regressions and validate the exact implemented profile, including the WEB-1 CSS format boundary, WEB-3 and WEB-4. Only explicit final approval permits replacing candidate status with a frozen declaration. npm publication remains a separate gate in [docs/publishing.md](docs/publishing.md).
24
+
25
+ ## P1 — web-native contract
26
+
27
+ Implement the accepted [web-native core contract](docs/web-native.md). These are remaining conformance tasks, not shipped capabilities. Keep existing working behavior until each replacement passes its acceptance checks; adopting the document does not itself change the wire schema or authorize a schema freeze.
28
+
29
+ ### Native CSS and optional providers
30
+
31
+ Checkpoint: a plain HTML/CSS project can be created, edited and rebuilt without a style engine; optional normalization preserves the requested CSS semantics and dependent projects cannot silently lose styling. Native HTML input is part of this stabilization gate: retain `core/tests/html-import.test.js` coverage for registry-independent admission, source preservation, listing policy and guarded re-import while implementing the remaining provider work.
32
+
33
+ - [ ] **WEB-1 — Give semantic style edits a native CSS path.** `styles` currently delegates to the active engine and throws `NO_STYLE_ENGINE` without one. With Tailwind, a pre-existing inline declaration can still win over the new utility (request 16px padding, retain 12px inline, render 12px). Separately, `codec.layered` wraps authored CSS in the components layer even without an engine: a blue token beats a later authored red `:root` declaration, unlike native CSS. Reordering `style` members `padding-left:20px`/`padding:10px` also changes Chromium's left padding from 10px to 20px while current deep comparison considers the models equal. Implement the approved native emission and ordered declarations through FORMAT-TRANSITION; these persisted-meaning sub-boundaries are not merely editor features. Make the owning style operation reconcile both representations, retain unrelated declarations and preserve states/variants. Verify `core/tests/model.test.js`, `core/tests/no-engine.test.js` and a browser witness for an imported inline-styled element; cover enabled/disabled engines and repeated normalization.
34
+ - [ ] **WEB-2 — Separate plugin composition and PEN conversion from native semantics.** Move concrete provider selection out of generic model/registry loading; the current engine and importer registries import built-ins directly. Resolve the standalone-entry/default-initialization and public-type boundary before moving registration: cold `./model`, `./engine` and `./plugins` currently receive bundled providers without the library root, and order/deduplication/exact-renderer behavior must survive any compatibility path. The model → engine → model source-contract cycle has an erased reverse edge, not an executable ESM cycle; do not use a runtime-cycle claim to justify a root-only bootstrap. Make PEN conversion produce ordinary HTML/SVG/CSS without requiring `declarationClass`; optional normalization runs after conversion. Verify native loading without optional providers, styled PEN conversion with no engine, and explicit default-plugin composition through `core/tests/html-import.test.js`, `core/tests/no-engine.test.js`, `core/tests/entrypoints.test.js` and `plugins/pen/tests/convert.test.js`.
35
+ - [ ] **WEB-3 — Define and enforce rendering dependencies and engine disable semantics.** The current project schema has no enforced dependency profile, and compiling utilities with no engine yields an empty stylesheet. First resolve release dependency reproducibility: local source/build tokens agree, but a clean tarball installation produced a different effective token. Trace the selected inputs and pin the intended release closure before freezing; do not weaken exact matching or silently rewrite requirements. Then integrate the opt-in versioned compiler descriptor through trusted host composition and the approved project `requires` representation, not a default-engine or class-name guess. Bind resource/inspector capabilities to the same effective provider before claiming complete project support; the current versioned descriptor is compiler-only. Retain draft helper/ambient-base behavior until cutover and add explicit migration rules. Diagnose missing required providers without guessing from classes. Disabling a dependent engine must materialize equivalent ordinary CSS through the accepted-state path or refuse without changing the project. Verify rebuild after deleting cache, missing-provider diagnostics, successful conversion and refused conversion with retained source/history/output in the no-engine and output suites plus a computed-style browser check.
36
+
37
+ ### Reversible projection and identity
38
+
39
+ Checkpoint: managed HTML edits map back to the correct native objects through guarded acceptance, without order-dependent overrides, accidental component detachment, resource rebasing or compiled-CSS duplication. Clean static export remains independently usable.
40
+
41
+ - [ ] **WEB-4 — Finish component identity and override semantics.** Resolve effective targets/aliases, reject conflicting peer writes, coalesce identical writes and preserve explicit nested-layer precedence independent of JSON member order; `button.text` and its text child's `content` currently produce different results when their keys are reordered. Enforce the approved consumer CSS/token scope and refuse duplicate nonempty expanded DOM IDs, including references such as `for`, ARIA ID lists and SVG fragments. Preserve valid instance selection/provenance across re-rendering. Verify `core/tests/components.test.js`, `core/tests/codec.test.js` and a browser reference/selection witness.
42
+ - [ ] **WEB-5 — Implement guarded reverse editing of managed HTML.** `scanOutputs()` currently diagnoses edited generated pages without importing them. First define the reversible projection/provenance for text IDs, nested instance addresses, resource rewrites and authored-versus-compiled CSS; do not substitute native external-HTML admission or clean export for that contract. Then admit expressible edits through the same validated writer/history path as tools and JSON, refuse ambiguous structural or stale edits while retaining authored bytes, and suppress own-write import loops. Build on WEB-4 identity and WEB-1 style semantics. Verify codec/component/runtime suites and `web/tests/run.mjs` for valid, invalid, conflicting and component-instance edits, plus retained clean static output.
43
+
44
+ ## P1 — browser-host implementation
45
+
46
+ The user authorized incremental implementation of the [browser runtime direction](docs/browser-runtime.md). Scope: shared portable semantics, a capability-truthful static demo and separately proven browser-agent connectivity; not unrelated product/assets work, a wholesale rewrite or additional format-freeze prerequisites. Continue through independently validated checkpoints. No publication, deployment or Service-Worker-as-public-MCP assumption.
47
+
48
+ - [ ] **BROWSER-DEMO — Prove a static-hosted, capability-truthful board demonstration.** Next provide a semantic prepared-preview path and hide/refuse mutation affordances using actual host capabilities rather than synthesizing HTTP routes. Compose the existing allowlisted public-page producer and read-session/snapshot seam with browser-local selection/observations, absent native History/source-update services and the real board UI under a repository prefix, without Node API/SSE rescue. Audit project/codec/assets/tool-schema dependencies only as needed; standalone-frame and opaque snapshot proofs do not establish whole-project acceptance, component expansion or rendering/resource support. Keep onboarding readable and the demo opt-in; no second authored landing or recursive editor iframe. Verify clipboard actions, private-project non-upload, accurate absent capabilities, first visit versus offline reload, versioned cache updates and startup/bundle cost. Offline editing additionally needs persistence/failure/download/reopen and multi-tab ownership evidence; do not infer it from a read-only demo.
49
+ - [ ] **BROWSER-AGENT — Validate browser-native tools and external-agent transport separately.** Inspect WebMCP support and demonstrate real tool registration/call/refusal/cancellation with a participating browser agent before adopting an adapter. The Community Group draft and a property probe are not interoperability proof. For ordinary external MCP clients, prototype only an explicitly paired local/extension bridge with origin/document/project/session capabilities and browser network-policy checks; no default cloud relay, wildcard CORS, localhost probing or Service-Worker-as-public-server claim. Preserve the existing operation vocabulary and authenticated preview boundary.
50
+ - [ ] **BROWSER-NODE22-COLD — Capture a reliable reproduction of a pre-model package-reader refusal.** One default-parallel focused run on Node 22.19 raised `ERR_INVALID_PACKAGE_CONFIG` for the valid repository manifest during the new cold-source import, before model execution. An isolated import, serial 125-check run, three controlled parallel runs and the full 569-check parallel unit suite passed; that does not establish the cause or a fix. Retain `artifacts/browser-model-portability-first-failure.log` and diagnostic logs. If it recurs, capture manifest identity/read/descriptor diagnostics and compare the unchanged baseline; do not add retry-until-green, weaken the cold-provider assertion or claim proven flakiness. Further causal work is gated on reproduction in a frozen checkout rather than repeated unchanged reruns; concurrent manifest edits occurred in this shared checkout and must be excluded before attributing the failure to Node or the model.
51
+
52
+ ## P1 — onboarding template initialization
53
+
54
+ Use the canonical self-contained `examples/landing/landing.divlet` for new-workspace onboarding without coupling the core to a marketing tree. Explicit paths, empty/no-demo creation and existing sources keep their meaning. The current source/cache/Pages contract is documented with the [examples](examples/README.md), not repeated here as open delivery.
55
+
56
+ - [ ] **SKILL-INSTALL — Finish the landing half of the two-step skill path.** README and `docs/agents.md` already carry `npx skills add llblab/divlet --skill divlet --global` followed by separate MCP registration, and a copied standalone skill is tested. Still open: the same two steps on `examples/landing/landing.divlet` (edit through the normal writer, then regenerate and check the tracked cache), and a run of the command itself against the public repository once it is usable. Do not install into the operator's global profile without explicit intent.
57
+ - [ ] **LANDING-SKILL — Deliver the reusable project landing workflow and CI kit.** Add `skills/divlet-landing/` with a project-scoped workflow, default `landing/landing.divlet`, a curated entry frame and tracked prepared cache. Respect an existing canonical landing, including this repo's `examples/landing/`; never create a duplicate source. Ship tested configurable CI/helper templates using one shared owner for freshness, public graph/link/fragment checks, fingerprinting and byte-for-byte `gh-pages` copying; exclude sidecars/private files and skip publication for unchanged public bytes. Confirm entry-frame selection semantics before adding a wire field: `id: index` already generates `index.html`, but arbitrary-ID entry selection remains a design choice. Verify the kit in an unrelated repository and a packed installation; template creation is not deployment authorization.
58
+ - [ ] **EXAMPLE-DEFAULT — Seed new workspaces from an independently owned template copy.** Repository demo opens the canonical project; general CLI/MCP/Pi empty-workspace startup still needs composition-owned initialization from the packaged source. Validate and exclusively create the copy, preserve resource bases and output isolation, honor `--no-demo` and never write into the installed master or replace user sources. Reconcile bootstrap docs and prove fresh/no-demo/existing-project behavior through every host plus a real packed installation. Keep importer/site/layout coverage intact; the removed authored PEN demos do not retire importer support. Do not replace the plural examples collection or duplicate the landing in core.
59
+
60
+ ## P1 — product
61
+
62
+ - [ ] **PANEL-PREFS-ORIGIN — Resolve cross-launch browser preference scope.** Panel widths are primary local preferences within one browser origin, but boards currently request an available port by default, so a restart/new board can have a different `localStorage` namespace. Decide the stable-origin or explicit preference-transfer contract before promising cross-launch/shared-project memory; do not claim localStorage crosses ports or add panel state to authored content. Preserve loopback/Host/Origin/CSP boundaries and one writer per project. Validate reopening at the same and a different port, multiple live projects, occupied ports and denied/cleared browser storage. This is a design boundary beyond in-origin width retention, not permission to add a shared preference service silently.
63
+ - [ ] **PREVIEW NAVIGATION — Close the remaining Play link gaps.** Same-page `#fragment` links scroll in place and a link to another artboard's page reveals and selects it and scrolls it to the fragment (`web/tests/preview-links.mjs`); the iframes stay loaded and nothing is swapped. A link to a page no artboard owns is named in a toast. Still open: `site` routes are not followed; the expanded view still owns real browsing. Do not persist token URLs, change canonical portable hrefs or trust an iframe after navigation. Verify in scaled-canvas Play and after returning to Select, with a Chromium witness beside the document-bound port boundary.
64
+ - [ ] **KEY FORWARDING — Remaining intermittent drops in the keyboard hand-off.** The dominant cause of `features.mjs` "Enter in a list item saves it and starts a new one" was not a lost key: the page keeps a render update back while a text edit is open, so an edit begun against the render the board last heard about (it learns renders from a lagging server-sent event) was committed with a stale `expectedRender` and refused as `RENDER_CONFLICT`; the refusal was swallowed and the typed text vanished. The bridge now names the held render when that element's text is unchanged in it (`web/tests/text-edit-render.mjs`); that failure went from 7/24 to 0/32 under a parallel load. Still open: one unexplained `features.mjs:32` miss in 56 loaded runs, and `controls.mjs` "rename with `R` and by double-clicking the label" (`.ab-rename` never appeared after `R`), neither reproduced on demand. A refused text commit still only produces a toast; decide whether it should keep the typed text recoverable.
65
+ - [ ] **TOAST UNDO TIMING — Do not let a background notice evict an actionable toast.** `features.mjs` multi-delete (`features.mjs:382`) fails about once in ten loaded runs: the toast log shows `2 artboards deleted` (with Undo) replaced ~90 ms later by `Pi has not started yet. Check the Pi terminal…`, the ask watchdog in `web/lib/ask.js` that fires nine seconds after any request the agent has not started on (the mock agent in the suite never does). Slow runs move that moment onto whatever the suite is doing. This is a product defect, not only a test race: a timer-driven notice replaces an Undo the user may be about to press (`web/board.js` rebuilds `#toast` for every toast and hides it after 7 s). Decide the rule (an actionable toast keeps its window and a lower-priority notice waits or goes to the Agent status instead), then keep the test's full assertion. Acknowledging requests in the test (busy true/false after the last ask) was tried and did not help: the watchdog is armed after the request resolves, and making every ask busy breaks the activity-plaque checks.
66
+ - [ ] **INSPECTOR SECTIONS — Refine each section's information layout and control density.** Border readback now understands the declarative `[border-width:Npx]` spelling (`plugins/tailwind/web/tw.js`, `appearance.mjs`); per-side declarative borders and non-px/variable widths still show as unset or as written. Audit colour/token and native attribute editors for semantic grouping, long values and compact label/control relationships; verify no-plugin and empty/mixed states rather than forcing every editor into the same grid. Refine remaining Layout/artboard controls where real usage exposes friction; prioritize frequent edits and remove redundant controls without sacrificing data. Keep numeric readability and stable section order while deciding which related fields should stay paired rather than forming arbitrary wide rows. Use the existing intrinsic grids and spacing container queries rather than another navigation strip or generic breakpoint. Verify artboard, single/multiple element and no-plugin states, selection/focus/scroll continuity, and desktop/tablet/390px/200% behavior through the toolbar/fluid suites and focused per-section Chromium witnesses. This is remaining per-section work, not a claim the entire inspector is finished.
67
+ - [ ] **TAB BRIDGE — Let the open tab belong to its panel.** With a panel open its tab could read as that panel's own header (the active tab's surface continuing into the panel, the other two inactive) instead of all three sitting on one solid bar. Today the bar is solid and the active tab is tinted (`--accent-soft`); this is a refinement of that chrome language, paired with the `data-panels` rule.
68
+
69
+ ## P1 — TypeScript migration
70
+
71
+ Complete the remaining migration under `strict` + `erasableSyntaxOnly`, preserving real source specifiers and Node's native type stripping. `npm test` starts with `tsc --noEmit`; `npm run build` produces publish-only JavaScript/declarations. Current configuration has `checkJs: false` and excludes `web/`: accepting a JavaScript import at a typed boundary is not full implementation/client checking. Remaining work follows this order, keeping `node scripts/source.mjs --test`, browser suites, `test:mcp`, `test:contract`, `test:pi` and `test:release` green without hiding pre-existing failures:
72
+
73
+ - [ ] **CORE TYPES — Convert the remaining JavaScript boundaries.** Type `core/lib/static.js` first (portable build/resource flow), then `runtime.js` (replace the hand-declared server-facing runtime shape with the real typed implementation). Treat `bridge.js` last and differently: its browser function is serialized with `Function#toString`, so prefer a typed wrapper around an unchanged self-contained body and convert only when the cost is justified. Accept `unknown` at boundaries, return validated data and preserve domain errors and behavior.
74
+ - [ ] **ADAPTERS AND PLUGINS — Convert `cli/**`, `mcp/**`, `pi/**` and `plugins/**`.** Pi (`extension.js` is the largest: type the Pi API surface it uses locally, do not depend on Pi types), MCP (`server.js`, `index.js`), CLI; then the plugins (`tailwind` styles/compile and `pen` projection/source/validate). Plugin `index.js` entry points stay thin.
75
+ - [ ] **TESTS — Convert the suites to `.ts`.** `core/tests/*.test.js` → `.ts` (native execution, no compile), the browser suites in `web/tests/`, adapter and plugin tests. Keep the assertions identical; this is where the typed model starts paying for itself (fixtures can be typed documents instead of literals).
76
+ - [ ] **BROWSER CLIENT — Type `web/` and keep the board served without a bundler.** The client is plain ES modules served as files, so it cannot be shipped as `.ts`: either compile `web/` to `dist/web` (the build already mirrors assets there) and serve the built copy in the installed package while developing against sources, or type it with `checkJs` + JSDoc `@import` from `core/lib/model.ts`. Decide once, then convert `web/lib/store.js`, `canvas.js`, `inspector.js`, `actions.js` and friends. The plugin client modules (`plugins/*/web/`) follow the same decision.
77
+ - [ ] **SKILL-A11Y — Read the skill as its own reader would.** The skill is now the entry point (2026-11-12) and is checked against the code, but nobody has measured whether an agent that has *only* the skill actually succeeds: the honest check is a run in a host with no divlet extension (register `divlet-mcp`, load the skill, ask for one change) and a note of where it hesitated. Suspects: the install section assumes the host can start a subprocess. The loop now says what a good `view:preview` answer is (non-null, matching version, overflow flags) and that `observation: null` is not success.
78
+ - [ ] **PI-ONLY FEATURES — Keep going where only the extension can reach.** The first one landed (2026-11-12): the Agent pane's model and thinking-level chips, advertised through `agent.controls`, applied by Pi, never stored in the project. Next candidates, each carrying the same shape (board state advertises it, the UI is absent without it, the host is the authority): (a) a model/thinking **preset** for board requests ("quick draft" vs. "careful pass") so a request can pick an effort without changing the session's own settings; (b) **queue visibility** — what Pi will answer next, taken from the session rather than the board's own count; (c) **session scope** — which Pi session/agent answered a board, so two boards do not silently share one conversation. Do not add a control without naming what the host does with it and what the board shows if the host refuses.
79
+
80
+ ## P2 — hardening
81
+
82
+ - [ ] **MCP REGISTRY — Finish scoping the board registry to its user.** Reads now ignore (and never delete) a registry file owned by another uid, so a shared home or a shared `PI_DIVLET_STATE_DIR` can no longer hand this process another account's board URL and token. Still open: the registry *directory* itself is not checked (another account that owns it could still replace entries it also owns, which the reader now rejects, but could make writes fail), and a per-uid socket would remove the shared-directory question entirely. Deliberately deferred: single-user local machines remain the supported case.
83
+
84
+ ## Gated
85
+
86
+ - [ ] **RELEASE-1 — Publish the validated scoped five-package 0.0.1 candidate after explicit approval.** The maintainer approved `@divlet/core` plus an independently owned `@divlet/web` after npm refused unscoped `divlet`; preserve adapters → core → web without runtime/type back-edges. Use the current flat-layout archives and integrity manifest in ignored `artifacts/releases/0.0.1-flat/`, without substituting bytes. The previous `0.0.1-scoped/` (`21907c1`) and original four-archive `0.0.1/` candidates remain historical; their archives/publisher are not the current release candidate. Publication remains external and gated: obtain explicit approval for the new five-archive candidate, keep OTPs/tokens private, recheck scope rights and conflicting registry versions, publish web → core → CLI/MCP/Pi, compare integrity and verify clean registry installs including an isolated real Pi profile. Follow [npm publication](docs/publishing.md); the current draft release is independent of future format-freeze work. Do not announce publication until verified. Git release refs/push and Pages deployment still need their explicit authorization. A live checkout's legacy Pi link must only be switched to built `pi` with operator approval; do not restart owners or discard session History as a package-check shortcut.
package/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ Implemented product outcomes live here. Contributor constraints are in [AGENTS.md](AGENTS.md), current contracts in [docs/](docs/README.md), and remaining work in [BACKLOG.md](BACKLOG.md).
4
+
5
+ ## Unreleased — 0.0.1 candidate
6
+
7
+ Preparing the approved five-package scoped candidate; publication and registry-install verification remain outstanding. A versioned archive is not a completed release.
8
+
9
+ Initial release of divlet: a local HTML/SVG/CSS layout board for agents. The native project format remains an unfrozen schema-1 draft; this release does not activate the approved future ordered-CSS/renderer profile or promise frozen compatibility.
10
+
11
+ ### Product and agent integration
12
+
13
+ - **One product, three real adapters.** `@divlet/core` supplies the model/runtime/server, bundled plugins and portable workflow skill. The separate `@divlet/web` owns the only browser client and portable host ports, without a core/Node dependency; the HTTP composition uses its public asset locator. `@divlet/cli`, `@divlet/mcp` and `@divlet/pi` own their commands and implementations, with exact matching core dependencies; core pins web. All five release versions match. CLI/MCP/Pi own their real packages directly in `cli/`, `mcp/` and `pi/`, beside core and web; workspace, build, source/installed entrypoints and documentation follow the flat topology without wrappers. The unscoped `divlet` npm name was refused by registry policy; the product, command and tool identities stay divlet. Node.js 22.19 or later is required; CLI and MCP do not require Pi.
14
+ - **Seven shared validated tools.** Pi and MCP use the same open/read/patch/structural-edit/frame/history/work-mark contract, revisions and refusal codes. Board requests carry the submitted selection snapshot and compact context rather than following later selection changes. MCP sampling and Pi model/thinking controls appear only when the host advertises them; session settings are not project content.
15
+ - **One live writer per project.** Other clients attach to the owning board and share its revisions and history. Importing the library or an adapter starts no service. Installed packages use compiled exports; checkout development runs TypeScript sources without a build.
16
+
17
+ ### Native projects and files
18
+
19
+ - **Real web content in independently owned files.** A `.divlet` contains frames, shared tokens/CSS, reusable components, property overrides and inert metadata. Valid external edits are accepted; invalid or stale candidates preserve the last working state. Discovery and the launcher distinguish exact project files, including same-named projects.
20
+ - **Bounded HTML admission and foreign-format conversion.** Static HTML imports use the core sanitizer; PEN interpretation belongs to its importer plugin. Conversion creates a non-overwriting sibling project and preserves the original. Markdown sites retain routed pages and shared templates.
21
+ - **Embedded resources with explicit identity.** Image uploads and declared files retain their bytes in a hierarchical Data-URL asset tree, materialized only as rebuildable output. Explicit `asset:` references preserve names, MIME and dependency bases; literal Data URLs stay inline. Existing filesystem inputs remain supported without silent absorption.
22
+ - **Owned static output and export.** Generated HTML/resources have per-project provenance, collision/link refusal and repairable sidecars. Supported local dependencies survive portable publication below a URL prefix. Clean per-frame HTML export embeds supported resources; ambiguous query, SVG-use, cycle or expanded-size semantics refuse rather than silently change meaning. Save a copy creates an independently identified project.
23
+ - **Device fonts use ordinary CSS and common assets.** Family selection preserves understood fallback stacks and inline priority. TTF/OTF, bounded WOFF 1 and restricted null-transform WOFF2 registration retain exact submitted bytes and append only proven disjoint companions. Upload and selection are separate Undo steps. Static CFF2 and independently encoded CFF1 WOFF fixtures exercise actual browser matching and portable loading.
24
+
25
+ ### Board and editing
26
+
27
+ - **Long-lived live artboards.** Lazy iframes retain identity through edits, zoom and selection. Expanded browsing is separate from authenticated editing; known page/fragment links reveal their artboards, and projected missing-page links report the unsupported destination. Browser-generated snapshots embed proven font bytes; stale or unprovable captures keep the live page instead of showing fallback-font text.
28
+ - **Direct, responsive inspection.** Layers, bottom breadcrumbs, one vertical inspector flow and adaptive toolbars expose layout, typography, appearance and metadata. Spacing uses outside-in box bands with independent side values and measured content dimensions. Border has two synchronized views and one writer. Controls retain whole values, keyboard access and useful narrow/200% layouts.
29
+ - **Guarded whole-project history.** Grouped and multi-step Undo/Redo validate the complete candidate, refuse overlapping later edits and persist before moving stack authority. Frame lifecycle/order replay preserves authored positions. History state points name proven stack endpoints, not audit sequence numbers.
30
+ - **Stable conversation and feedback.** The agent pane follows arrivals near the bottom without stealing older reading positions. Activity/Stop moves as one surface when the sidebar folds. Delayed agent-start notices defer for a bounded interval to preserve the ordinary Undo window, and deleted artboards no longer publish queued measurements.
31
+
32
+ ### Safety and boundaries
33
+
34
+ - **Local, bounded access.** The board binds to loopback with token-scoped routes, Host/Origin checks, CSP and request bounds. Filesystem operations use regular-file/no-link checks, byte/count budgets and non-overwriting publication. Trusted project access is not an arbitrary HTTP filesystem route.
35
+ - **Authenticated previews.** Editing traffic uses a current document-bound MessagePort with capability rotation, sandboxing and referrer protection. A matching iframe window alone is not authentication. Stale observations, captures and async reads cannot replace newer accepted state.
36
+ - **Explicit current limits.** Arbitrary application scripts, managed-HTML reverse editing, an offline browser writer and automatic public-safe snapshot curation are not implemented. Transformed WOFF2, font collections/optional container blocks, ambiguous face merging, wider linked/imported/dynamic font capture and native external SVG-use remain refused or gated. Variable CFF2 and arbitrary font-program compatibility are not established by static fixtures.
37
+ - **Separate evidence layers.** Source/unit/browser, local installed-tarball, registry-install, static-host and format-freeze evidence are distinct. Strict Node/core types and a small checked client transport slice do not imply whole-client typing. The portable browser semantic slice is not a complete offline editor.
38
+
39
+ Pre-release implementation and retired-experiment history remains in Git. See [testing](docs/testing.md) for executable checks and their coverage limits; future work is tracked only in the backlog.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 divlet contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,173 @@
1
- # Temporary Holding Version
1
+ # divlet
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Design in the browser. Change it in conversation.**
4
+
5
+ A local layout board for coding agents. Work with real HTML in live artboards, reuse components across pages, and make changes through Pi, MCP or the project file. The browser renders the layout; divlet handles structure, selection, validation and history.
6
+
7
+ [Quick start](#quick-start) · [Connect an agent](#connect-an-agent) · [Board guide](docs/board.md) · [Documentation](docs/README.md)
8
+
9
+ > **0.0.1 · release candidate.** Scoped-package preparation is in progress after npm refused the unscoped name; no package is published yet. The native project format remains a pre-freeze draft; keep backups because future versions may require explicit conversion.
10
+
11
+ ## Real pages, not pictures of pages
12
+
13
+ - **See the actual layout.** Each artboard is an iframe with real HTML, locally compiled Tailwind and CSS. Resize it to check responsive behavior.
14
+ - **Point, then ask.** Select an element and ask your connected agent to change it. Pi and MCP use the same seven validated tools.
15
+ - **Build once, reuse.** Components, instance overrides, shared tokens and project CSS keep related pages connected.
16
+ - **Keep your work local.** The board binds to loopback. Projects are files in your workspace, with autosave, history and undo—not a hosted design service.
17
+
18
+ ## Quick start
19
+
20
+ Requires **Node.js 22.19 or later**. Pi is optional.
21
+
22
+ Open a standalone board:
23
+
24
+ ```bash
25
+ npx -y @divlet/cli@0.0.1 /absolute/path/to/workspace
26
+ ```
27
+
28
+ Or run from a source checkout:
29
+
30
+ ```bash
31
+ cd /path/to/divlet
32
+ npm ci --legacy-peer-deps
33
+ npm start -- /absolute/path/to/workspace
34
+ ```
35
+
36
+ Open the URL printed in the terminal. An empty workspace gets `project.divlet` with a starter artboard; an existing project opens directly, and a folder with several projects opens the launcher.
37
+
38
+ `npm run demo` opens the real onboarding page, [examples/landing/landing.divlet](examples/landing/landing.divlet). Its tracked, derived cache supplies GitHub Pages; see [editing and rebuilding the landing](examples/README.md). `npm run demo:site` opens the Markdown handbook in `examples/handbook/`; `examples/studio/` holds a separate layout scenario. Pencil import remains supported, but no authored PEN demo is bundled.
39
+
40
+ **First moves:** `V` selects, `P` lets the page behave normally, `L` opens Layers and `H` opens History. `Ctrl K` finds commands; `?` shows shortcuts. See the [board guide](docs/board.md) for direct editing, the inspector and touch controls.
41
+
42
+ ## Connect an agent
43
+
44
+ ### Pi
45
+
46
+ Install the Pi adapter:
47
+
48
+ ```bash
49
+ pi install npm:@divlet/pi@0.0.1
50
+ ```
51
+
52
+ For checkout development, load the source adapter for a one-off session instead (no build needed):
53
+
54
+ ```bash
55
+ NODE_OPTIONS=--conditions=divlet-source pi -e /absolute/path/to/divlet/pi/index.js
56
+ ```
57
+
58
+ For a persistent local package installation, first run `npm run build`, then `pi install /absolute/path/to/divlet/pi`. Choose one method; never load both.
59
+
60
+ Open Pi in your workspace, run `/reload` if it is already running, then:
61
+
62
+ ```text
63
+ /divlet
64
+ ```
65
+
66
+ Open the returned board URL, select something and press `A` to ask about it. Pi supplies the tools, the bundled divlet skill and the Agent conversation. Replies stay in Agent; accepted edits appear in History.
67
+
68
+ ### MCP
69
+
70
+ Two separate steps: the workflow skill teaches the agent the loop, the MCP server gives it the tools. Installing the skill registers no tools.
71
+
72
+ **1. Install the skill** (user-level, so every project sees it; the installer asks which agents to target):
73
+
74
+ ```bash
75
+ npx skills add llblab/divlet --skill divlet --global
76
+ ```
77
+
78
+ Drop `--global` to install into the current project only. Skip this step under Pi: the extension below already registers the same bundled skill, and installing it twice makes two copies compete.
79
+
80
+ **2. Register the server.** Use the npm adapter:
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "divlet": {
86
+ "command": "npx",
87
+ "args": ["-y", "@divlet/mcp@0.0.1", "--cwd", "/absolute/path/to/workspace"]
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ Call `open_divlet` and open its returned URL. No Pi installation is needed. Clients that support **sampling** can also answer requests typed in the board's Agent tab.
94
+
95
+ [Agent integration guide](docs/agents.md) → Alternative Pi installation, MCP options, the workflow skill and JavaScript entry points.
96
+
97
+ <details>
98
+ <summary><strong>Package roles and alternative installation</strong></summary>
99
+
100
+ **`@divlet/core`** owns the library, model/runtime, server, built-in plugins and shared workflow skill. **`@divlet/web`** owns browser assets and portable host ports, without a dependency back on core. Core's HTTP composition uses its public asset locator. Three real adapter packages own their implementation and commands: **`@divlet/cli`**, **`@divlet/mcp`** and **`@divlet/pi`**. Each adapter pins the exact matching core version; core pins web. All five packages share one product release. See the [web package](https://github.com/llblab/divlet/blob/main/web/README.md).
101
+
102
+ ```bash
103
+ # Open a standalone board:
104
+ npx -y @divlet/cli ./my-project
105
+
106
+ # Launch the server from an MCP host:
107
+ npx -y @divlet/mcp --cwd ./workspace
108
+
109
+ # Or install both commands globally:
110
+ npm install -g @divlet/cli @divlet/mcp
111
+ ```
112
+
113
+ For Pi, run `pi install npm:@divlet/pi@0.0.1`. Shared setups should pin `@divlet/cli@0.0.1`, `@divlet/mcp@0.0.1` and `npm:@divlet/pi@0.0.1`. The root package has no commands or Pi manifest; there is no wrapper layer.
114
+
115
+ See [npm publication](docs/publishing.md) for version pinning, package identity and release gates.
116
+
117
+ </details>
118
+
119
+ ## A project you can keep
120
+
121
+ ```text
122
+ workspace/
123
+ ├─ project.divlet Frames, components, tokens, CSS and embedded resources
124
+ ├─ assets/ Legacy local inputs, when still referenced
125
+ └─ .divlet/project/ Generated HTML, metadata and build assets
126
+ ```
127
+
128
+ The `.divlet` JSON file is the source. Edit it through the board, agent tools or your editor: valid external changes appear on the board, and invalid ones leave the last working state visible. New image uploads are embedded in that file; explicitly named resources are materialized only as rebuildable output. Existing local-file references still work without silent conversion. Generated output belongs to one project and does not overwrite unowned files.
129
+
130
+ Open a Pencil `.pen` or static `.html` file to import it into a sibling `.divlet` project. The source stays untouched and an existing destination is not overwritten. The generated output can also be served independently as a static site.
131
+
132
+ [Project format](docs/format.md) · [Files, imports and output](docs/board.md#where-projects-live) · [Markdown sites](docs/site.md)
133
+
134
+ ## Agent tools
135
+
136
+ **Read → edit → verify.** The agent reads the current frame and version, submits a labelled change, then checks the result. A stale version is refused rather than silently overwriting newer work.
137
+
138
+ - **Open and inspect:** `open_divlet`, `read_divlet`.
139
+ - **Change the layout:** `patch_divlet`, `edit_divlet`, `frame_divlet`.
140
+ - **Explain and recover:** `mark_divlet`, `undo_divlet`.
141
+
142
+ The [workflow skill](skills/divlet/SKILL.md) teaches the loop; the [tool guide](docs/agents.md#agent-tools) covers views, components, overrides and refusal codes.
143
+
144
+ ## Current boundaries
145
+
146
+ - **Static web layouts, not application code.** HTML, SVG, CSS and Tailwind classes; no scripts, event handlers or build directives.
147
+ - **Property overrides, not arbitrary subtree replacement.** Change a component reference or detach the instance for structural changes.
148
+ - **Generated HTML is not an editing round trip yet.** Hand edits are reported and preserved on regeneration, not imported into the project. Make persistent changes in the model.
149
+ - **One live owner per project.** Other clients attach to its board and share revisions; independent writers are not supported.
150
+
151
+ ## Go deeper
152
+
153
+ - [Board guide](docs/board.md) — Canvas, selection, panels, components, files and static output.
154
+ - [Agent integration](docs/agents.md) — Pi, MCP, tools and the JavaScript API.
155
+ - [Format reference](docs/format.md) — The JSON model and generated files.
156
+ - [Architecture](docs/architecture.md) — Core, plugins, adapters and trust boundaries.
157
+ - [Web-native core contract](docs/web-native.md) — Accepted design direction; implementation gaps remain in the backlog.
158
+ - [Documentation index](docs/README.md) — All guides, examples and contributor references.
159
+
160
+ ## Development
161
+
162
+ These checks run from the source checkout. Frontend unit and browser suites live in `web/tests/`; server/model unit suites live in `core/tests/`. Tests are neither served as client assets nor shipped in the npm package.
163
+
164
+ ```bash
165
+ npm test # Type checking and unit tests
166
+ npx playwright install chromium
167
+ npm run test:browser:all # Browser suites
168
+ npm run test:release # Install local tarballs; publish nothing
169
+ ```
170
+
171
+ See [testing and validation](docs/testing.md) for focused suites and prerequisites. Contributor rules live in [AGENTS.md](AGENTS.md); remaining work in [BACKLOG.md](BACKLOG.md); completed changes in [CHANGELOG.md](CHANGELOG.md).
172
+
173
+ [MIT license](LICENSE). Local files. Real HTML. Your agent, your workflow.
@@ -0,0 +1,3 @@
1
+ export { openRuntime } from "./lib/runtime.js";
2
+ export { startServer } from "./lib/server.ts";
3
+ export { launch, makeHost, resolveOpenFile } from "./lib/launch.ts";
@@ -0,0 +1,4 @@
1
+ // The Divlet core's public API: the runtime (format, HTML codec, web editor server) and the ways of opening a project.
2
+ export { openRuntime } from './lib/runtime.js';
3
+ export { launch, makeHost, resolveOpenFile } from "./lib/launch.js";
4
+ export { startServer } from "./lib/server.js";
@@ -0,0 +1,21 @@
1
+ export declare const ASSET_REFERENCE_LIMITS: Readonly<{
2
+ path: 2048;
3
+ depth: 64;
4
+ segmentBytes: 255;
5
+ suffix: 2048;
6
+ encoded: number;
7
+ }>;
8
+ /** A decoded authored path and an opaque per-use query/fragment; the suffix never participates in asset lookup. */
9
+ export interface AssetReference {
10
+ readonly path: string;
11
+ readonly suffix: string;
12
+ }
13
+ /** One declared tree key, with the same portable rules at storage and reference boundaries. */
14
+ export declare function validateAssetName(name: unknown): asserts name is string;
15
+ export declare function validateAssetPath(path: unknown): asserts path is string;
16
+ /** Encode an exact declared path; no Unicode normalization, alias interning or filesystem interpretation. */
17
+ export declare function assetReference(path: unknown, suffix?: unknown): string;
18
+ /** Recognize only the explicit scheme. Ordinary file/network URLs and literal Data URLs remain outside this codec. */
19
+ export declare function readAssetReference(input: unknown): AssetReference | null;
20
+ /** Resolve a relative URL inside a declared text asset, never against a filesystem or network base. */
21
+ export declare function resolveAssetDependency(basePath: unknown, input: unknown): string;