@kohaku-ui/renderer-wc 0.3.0 → 0.4.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.
- package/CHANGELOG.md +136 -0
- package/dist/implement.d.ts +37 -0
- package/dist/implement.d.ts.map +1 -0
- package/dist/implement.js +44 -0
- package/dist/implement.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/kohaku-surface.d.ts +38 -1
- package/dist/kohaku-surface.d.ts.map +1 -1
- package/dist/kohaku-surface.js +144 -6
- package/dist/kohaku-surface.js.map +1 -1
- package/dist/parts/action-button.d.ts.map +1 -1
- package/dist/parts/action-button.js +26 -3
- package/dist/parts/action-button.js.map +1 -1
- package/dist/parts/form.d.ts.map +1 -1
- package/dist/parts/form.js +15 -1
- package/dist/parts/form.js.map +1 -1
- package/dist/parts/list.js +1 -1
- package/dist/parts/list.js.map +1 -1
- package/dist/parts/spreadsheet.d.ts.map +1 -1
- package/dist/parts/spreadsheet.js +23 -3
- package/dist/parts/spreadsheet.js.map +1 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +37 -5
- package/dist/tree.js.map +1 -1
- package/dist/types.d.ts +27 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,141 @@
|
|
|
1
1
|
# @kohaku-ui/renderer-wc
|
|
2
2
|
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#64](https://github.com/yosuque/kohaku/pull/64) [`b1a7af7`](https://github.com/yosuque/kohaku/commit/b1a7af7f39f09f2f088298098eaae618b32e363e) Thanks [@yosuque](https://github.com/yosuque)! - `implement` and `implementWc` parse a node's props against the part's schema once per `node.props` object instead of on every render or rebuild, and emit the dev-mode schema-mismatch warning once per props object (design.md decision 68). Rendered output is unchanged.
|
|
8
|
+
|
|
9
|
+
- [#66](https://github.com/yosuque/kohaku/pull/66) [`0a2a43b`](https://github.com/yosuque/kohaku/commit/0a2a43b5ea0cbe90df1bbe53af9137452654e477) Thanks [@yosuque](https://github.com/yosuque)! - `action.button` and `presentForm` now show the governed-action phases that stop an action before it commits: a rejected payload (`invalid`, `role="alert"`) and a declined confirmation or pending approval (`awaitingApproval`, `role="status"`). renderer-core adds `actionPhaseNotice` and the `RendererMessages.actionInvalid` / `actionAwaiting` strings (design.md decisions 62-64); `<kohaku-surface>` also clears a form's previous submit message when a new submit starts, as the React form does.
|
|
10
|
+
|
|
11
|
+
- [#66](https://github.com/yosuque/kohaku/pull/66) [`4072ca8`](https://github.com/yosuque/kohaku/commit/4072ca8c86f28c2bb79abbfc0483b2e6242b7768) Thanks [@yosuque](https://github.com/yosuque)! - Editable `presentSpreadsheet` cells now run the governed-action pre-check in both renderers (a value the `actionManifest` rejects stays in edit mode with `aria-invalid`), and an optimistic cell edit is discarded again when its `cellEdit` invoke ends `invalid`, `awaitingApproval` or `failed`, so the grid shows the server value. renderer-core adds `revertCellEdit`; renderer-react's `useInvokeAction().invoke` takes an optional `onPhase` callback (design.md decisions 62-64).
|
|
12
|
+
|
|
13
|
+
- [#66](https://github.com/yosuque/kohaku/pull/66) [`d16f7b3`](https://github.com/yosuque/kohaku/commit/d16f7b367ec62be4875ac18b42fd8be4d9da5c33) Thanks [@yosuque](https://github.com/yosuque)! - `<kohaku-surface>` reads `actionManifest`, `confirm` and `requestApproval` live at invoke time instead of rebuilding the mounted tree when one is reassigned. Assigning a spec and its manifest back to back no longer tears down an L2 sandbox iframe twice, and an inline `confirm` arrow no longer causes a rebuild on every assignment.
|
|
14
|
+
- Updated dependencies [[`a80f17a`](https://github.com/yosuque/kohaku/commit/a80f17a8ac304def877d23df9e7b8e37ef5c7396), [`e250463`](https://github.com/yosuque/kohaku/commit/e2504639145c3baacd1843c72512e8abf4f21b08), [`cb6e91a`](https://github.com/yosuque/kohaku/commit/cb6e91afdbbc083587cd32941ebac07b0151753a), [`171103f`](https://github.com/yosuque/kohaku/commit/171103f58ab87ff98daad8cb12901d417079d76b), [`b712fef`](https://github.com/yosuque/kohaku/commit/b712fef7404c6af1ca6ff726890eb6ddfd44dbfc), [`003e360`](https://github.com/yosuque/kohaku/commit/003e3605159973f6837fccf87f83691b40839483), [`5bb982e`](https://github.com/yosuque/kohaku/commit/5bb982e2fca7d4c609d5b99b6dc71bac95dbfe55), [`f34568b`](https://github.com/yosuque/kohaku/commit/f34568bb40e3c136bfc74e9c7d10c50c812f7e18), [`06a724e`](https://github.com/yosuque/kohaku/commit/06a724e046a432e8c64af217911bb2683ab8ca9d), [`71e17f9`](https://github.com/yosuque/kohaku/commit/71e17f9d970e01abaa8ebdf967054ad468555b56), [`ab25ddc`](https://github.com/yosuque/kohaku/commit/ab25ddc5691e216e6d5d027920a0a9abbc8f4207), [`206b95b`](https://github.com/yosuque/kohaku/commit/206b95b425800ec5af8f7e9ac203a8be70fd8d0b), [`343ccd7`](https://github.com/yosuque/kohaku/commit/343ccd7a71d4370473640dce94f1e2e2a821b39d), [`7c92cc4`](https://github.com/yosuque/kohaku/commit/7c92cc433e40a88f3b43eaeba7fd0af1a8755cad), [`a936266`](https://github.com/yosuque/kohaku/commit/a9362668b281564bc09a3b4a20233f8e1294bf41), [`0a2a43b`](https://github.com/yosuque/kohaku/commit/0a2a43b5ea0cbe90df1bbe53af9137452654e477), [`f8ecb4c`](https://github.com/yosuque/kohaku/commit/f8ecb4c71ba780378849c27c8ccdc6a14b31dcdc), [`4072ca8`](https://github.com/yosuque/kohaku/commit/4072ca8c86f28c2bb79abbfc0483b2e6242b7768)]:
|
|
15
|
+
- @kohaku-ui/registry@0.4.1
|
|
16
|
+
- @kohaku-ui/spec-core@0.4.1
|
|
17
|
+
- @kohaku-ui/data-binding@0.4.1
|
|
18
|
+
- @kohaku-ui/renderer-core@0.4.1
|
|
19
|
+
- @kohaku-ui/sandbox@0.4.1
|
|
20
|
+
|
|
21
|
+
## 0.4.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- [#60](https://github.com/yosuque/kohaku/pull/60) [`5f1bbbd`](https://github.com/yosuque/kohaku/commit/5f1bbbd09fe1edb984a7b0f5a0c5212c3da628ea) Thanks [@yosuque](https://github.com/yosuque)! - Adds a Compliance Evidence Pack export (`@kohaku-ui/lineage`'s new `evidence` module; `kohaku evidence
|
|
26
|
+
keygen`/`export`/`verify`) and opt-in AI-generation disclosure for both renderers (design.md [#66](https://github.com/yosuque/kohaku/issues/66)/[#67](https://github.com/yosuque/kohaku/issues/67)).
|
|
27
|
+
|
|
28
|
+
**Evidence Pack** (`@kohaku-ui/lineage`): `buildEvidencePack` assembles a normalized, Ed25519-signed
|
|
29
|
+
export of the lineage log (`events.jsonl`), a governance-decision index (`approvals.jsonl`:
|
|
30
|
+
`component.reviewed`/`published`/`withdrawn`, `intent.fixated`/`unfixated`), promotion/fixation
|
|
31
|
+
snapshots, and the referenced component HTML artifacts, plus `manifest.json` and a detached signature
|
|
32
|
+
(`manifest.sig`). `EvidenceManifestSchema` is new but deliberately not part of `spec/schemas` — it
|
|
33
|
+
describes an export format for auditors, not a wire type. Ed25519 signing uses `globalThis.crypto.subtle`
|
|
34
|
+
(no new runtime dependency for TS); an artifact whose recorded hash does not match its own content is
|
|
35
|
+
still exported, recorded as a non-fatal warning rather than aborting the export.
|
|
36
|
+
|
|
37
|
+
**CLI** (`@kohaku-ui/cli`): `kohaku evidence keygen --out-dir <dir>` generates an Ed25519 keypair (private
|
|
38
|
+
key file mode 0600). `kohaku evidence export (--data-dir <dir> | --rest <baseUrl> [--header k:v])
|
|
39
|
+
[--tenant <id>] --since --until --private-key <pem> --out <dir> [--allow-incomplete]` builds and signs a
|
|
40
|
+
pack from a local `StoragePort` data directory or, over REST, from the existing `KohakuClient` surface
|
|
41
|
+
(`lineagePages`/`promotions.list`/`fixations.list`) — the REST source leaves `fixations.jsonl` empty with
|
|
42
|
+
a recorded warning, since `GET /fixations` does not expose enough fields to reconstruct a full
|
|
43
|
+
`FixationRecord`. `kohaku evidence verify <dir> --public-key <pem>` checks the manifest schema, the
|
|
44
|
+
signature, and every file's hash/size, and reports an independent artifact-hash cross-check as non-fatal
|
|
45
|
+
`mismatches`; exit code 0 valid / 1 invalid / 2 usage error. `@kohaku-ui/lineage` and
|
|
46
|
+
`@kohaku-ui/storage-memory` move from `cli`'s devDependencies to dependencies.
|
|
47
|
+
|
|
48
|
+
**AI-generation disclosure** (`@kohaku-ui/renderer-core`, `@kohaku-ui/renderer-react`,
|
|
49
|
+
`@kohaku-ui/renderer-wc`): `deriveDisclosure(provenance)` (renderer-core) derives a disclosure level
|
|
50
|
+
(`"ai-generated"` for tier L1/L2, `"ai-assisted-reviewed"` for a fixated tier-L0 Spec, `"none"` otherwise
|
|
51
|
+
— never encoded on the wire) and the corresponding `data-kohaku-disclosure`/`data-kohaku-tier`/
|
|
52
|
+
`data-digital-source-type` (IPTC Digital Source Type) attributes. `SpecView` gains a `disclosure?: "off" |
|
|
53
|
+
"attributes" | "label"` prop (renderer-react; also exports `useDisclosure`/`KohakuDisclosureLabel`), and
|
|
54
|
+
`<kohaku-surface>` gains a matching `disclosure` attribute (renderer-wc, applied to the host element,
|
|
55
|
+
with the visible label — `"label"` mode only — inside the shadow root). Both default to `"off"`: existing
|
|
56
|
+
DOM output is unchanged unless a host opts in.
|
|
57
|
+
|
|
58
|
+
See [docs/user-guide.md](../docs/user-guide.md)'s "Compliance Evidence Pack and AI-generation disclosure"
|
|
59
|
+
section for usage, EU AI Act Article 50 context (not legal advice), and a PII caution for exported Intent
|
|
60
|
+
`params`/request text.
|
|
61
|
+
|
|
62
|
+
- [#62](https://github.com/yosuque/kohaku/pull/62) [`cc17b7b`](https://github.com/yosuque/kohaku/commit/cc17b7bc3c96e49b1b74197ac20cd7a3d8ee0b47) Thanks [@yosuque](https://github.com/yosuque)! - Add Governed Actions: Human-In-The-Loop tiers for `DomainPort` write operations (design.md [#62](https://github.com/yosuque/kohaku/issues/62)/[#63](https://github.com/yosuque/kohaku/issues/63)/[#64](https://github.com/yosuque/kohaku/issues/64);
|
|
63
|
+
SPEC §5's ACT-PRM-001/ACT-APR-001/ACT-CNF-001, LIN-ACT-001, §6.2's MCPAPP-ACT-001).
|
|
64
|
+
|
|
65
|
+
An operation may declare `tier` (`"auto"` (default) / `"confirm"` / `"approve"`) and `paramsSchema`
|
|
66
|
+
(kohaku's own closed JSON Schema subset — `type`, `properties`, `required`,
|
|
67
|
+
`additionalProperties: false`, `enum`, `minimum`/`maximum`, `minLength`/`maxLength`, `items`, `maxItems`,
|
|
68
|
+
`x-message`; deliberately no `pattern`, to avoid both ReDoS and a JS/Python regex-dialect mismatch).
|
|
69
|
+
`spec-core`'s `validateActionParams`/`assertValidActionParamsSchema` (env-neutral, dependency-free, pinned
|
|
70
|
+
byte-for-byte against the Python port via the cross-language golden) enforces the schema before
|
|
71
|
+
`DomainPort.invoke` ever runs, on every write surface alike: REST's `POST /binding/action`, MCP's
|
|
72
|
+
`${prefix}_action`, and the client-side `preflightAction` check `renderer-core` runs before either.
|
|
73
|
+
|
|
74
|
+
`"approve"`-tier actions are gated by a new stateless, short-lived HMAC-signed `ApprovalPort`
|
|
75
|
+
(`@kohaku-ui/authz-hmac`'s `createHmacApprovalPort`, `"kohaku-approval.v1."`-prefixed tokens, 300s default
|
|
76
|
+
TTL) bound to `(action, payloadHash, requesterId, tenant)`; self-approval is refused at issuance, and a
|
|
77
|
+
verification failure the host cannot classify is treated as a denial (fail-closed). An optional
|
|
78
|
+
`ApprovalStore` adds single-use enforcement. REST gains `POST /approvals` (mints a token as an authorized
|
|
79
|
+
approver, governance kind `action.approve`) and both the REST body and the MCP action tool's input gain
|
|
80
|
+
optional `confirmed`/`approval` fields; a gate failure is `422 ACTION_PARAMS_INVALID` / `403
|
|
81
|
+
APPROVAL_REQUIRED` on REST (with `error.issues`/`error.approval`) and the MCP structured-tool-error
|
|
82
|
+
equivalent.
|
|
83
|
+
|
|
84
|
+
A compose response optionally carries an **Action manifest** (REST's `actions?` on
|
|
85
|
+
`/compose`/`/events`/`event: spec`; MCP's `_meta["kohaku/actions"]`) mapping each governed action name to
|
|
86
|
+
`{tier, paramsSchema?, confirmMessage?}` — placed outside the `UISpec` itself, next to the capability, so
|
|
87
|
+
it never affects `specHash` or the cache key. `renderer-core`'s `preflightAction` consults it client-side
|
|
88
|
+
before a write round-trip; `renderer-react`/`renderer-wc`/`mcp-renderer` thread `confirm`/`requestApproval`
|
|
89
|
+
hooks through (`renderer-react` ships a `globalThis.confirm`-backed default for the `"confirm"` tier; there
|
|
90
|
+
is no framework-neutral default for `"approve"`, so that tier stays gated until a product wires its own
|
|
91
|
+
hook). `@kohaku-ui/client`/`@kohaku-ui/data-binding` gain typed `ACTION_PARAMS_INVALID`/`APPROVAL_REQUIRED`
|
|
92
|
+
error codes and `confirmed`/`approval` request options. A host that records action outcomes to lineage does
|
|
93
|
+
so under a distinct `action.*` event family (`action.invoked`/`action.denied`/`action.approvalRequested`/
|
|
94
|
+
`action.approved`), carrying `payloadHash` but never the payload's own field values.
|
|
95
|
+
|
|
96
|
+
The Python port (`python/kohaku`) mirrors the full surface (`kohaku.spec.action_params`,
|
|
97
|
+
`kohaku.host_core.action_gate`/`action_audit`, `POST /approvals`, the MCP action-tool gate), and
|
|
98
|
+
`apps/sample-api` / `python/examples/sales-api` demonstrate both tiers end to end (`annotate`: confirm,
|
|
99
|
+
`publish`: approve) — see the [user guide](../docs/user-guide.md)'s "Governed actions: tiers" section.
|
|
100
|
+
|
|
101
|
+
**Behavior changes to check when upgrading.** (1) `POST /binding/action` and the MCP action tool now
|
|
102
|
+
reject, before `DomainPort.invoke` runs, any action that is not in the DomainPort's own
|
|
103
|
+
`listOperations()` — previously such an action was invoked ungated. It is rejected with the same response
|
|
104
|
+
as a missing write scope and recorded as `action.denied`. A product whose `listOperations()` omits an
|
|
105
|
+
operation it still expects to be invoked must declare it. (2) `validateActionParams` rejects a payload
|
|
106
|
+
property named `__proto__`, `constructor` or `prototype` at any depth (issue code `unsafeKey`), whatever
|
|
107
|
+
the schema's `additionalProperties` says.
|
|
108
|
+
|
|
109
|
+
- [#54](https://github.com/yosuque/kohaku/pull/54) [`bd2d484`](https://github.com/yosuque/kohaku/commit/bd2d484826bec86c78dcb720502740494a672ea8) Thanks [@yosuque](https://github.com/yosuque)! - Adds a typed way to register a product-specific part, so its `{type, version, propsSchema}` can live in one
|
|
110
|
+
`ComponentDefinition` instead of being re-typed as string literals at the registration call site (design.md
|
|
111
|
+
[#68](https://github.com/yosuque/kohaku/issues/68)).
|
|
112
|
+
|
|
113
|
+
`@kohaku-ui/renderer-react`: `implement(def, Component)` wraps a component whose props are inferred from
|
|
114
|
+
`def.propsSchema` (`z.infer`) — no `node.props["x"] as T` cast needed — and returns an entry consumed by the
|
|
115
|
+
new `ImplRegistry.use(entry)`. The existing `register(type, version, component)` / `ImplProps` keep working
|
|
116
|
+
unchanged for parts that have no static `ComponentDefinition` (e.g. a promoted part's per-artifact schema).
|
|
117
|
+
|
|
118
|
+
`@kohaku-ui/renderer-wc`: `<kohaku-surface>` gains a public `registerPart(type, version, builder)` (it was
|
|
119
|
+
previously private with no way for a host to register anything beyond the core catalog), plus `getPartVersion`
|
|
120
|
+
for introspection, and `implementWc(def, builder)` is the typed counterpart of `implement` for a `PartBuilder`.
|
|
121
|
+
|
|
122
|
+
Both `implement` and `implementWc` parse a node's props against the schema **unconditionally, in every
|
|
123
|
+
environment** (`propsSchema.safeParse` is also what materializes a `.default()`-ed prop the Spec omits, not
|
|
124
|
+
just a validation nicety, so it never skips in production) — on success the component receives the parsed
|
|
125
|
+
value, on failure it receives the raw (unvalidated) props instead (fail-open: a malformed prop degrades the
|
|
126
|
+
part's own display rather than the whole surface). Only the diagnostic — a `console.warn` on a mismatch — is
|
|
127
|
+
gated by environment: by default it fires outside a `NODE_ENV=production` build; pass `{ validate }` to force
|
|
128
|
+
the warning on or off regardless of environment.
|
|
129
|
+
|
|
130
|
+
### Patch Changes
|
|
131
|
+
|
|
132
|
+
- Updated dependencies [[`fcd4eb7`](https://github.com/yosuque/kohaku/commit/fcd4eb7c8c6608030d4f9045648a305fa2e5992f), [`5f1bbbd`](https://github.com/yosuque/kohaku/commit/5f1bbbd09fe1edb984a7b0f5a0c5212c3da628ea), [`cc17b7b`](https://github.com/yosuque/kohaku/commit/cc17b7bc3c96e49b1b74197ac20cd7a3d8ee0b47), [`730e625`](https://github.com/yosuque/kohaku/commit/730e62584b249056078792f6f646019cb225b049), [`730e625`](https://github.com/yosuque/kohaku/commit/730e62584b249056078792f6f646019cb225b049), [`730e625`](https://github.com/yosuque/kohaku/commit/730e62584b249056078792f6f646019cb225b049), [`5d167cb`](https://github.com/yosuque/kohaku/commit/5d167cb386cc1f91102644f8a99bd5b5c2949ce0)]:
|
|
133
|
+
- @kohaku-ui/registry@0.4.0
|
|
134
|
+
- @kohaku-ui/renderer-core@0.4.0
|
|
135
|
+
- @kohaku-ui/spec-core@0.4.0
|
|
136
|
+
- @kohaku-ui/data-binding@0.4.0
|
|
137
|
+
- @kohaku-ui/sandbox@0.4.0
|
|
138
|
+
|
|
3
139
|
## 0.3.0
|
|
4
140
|
|
|
5
141
|
### Patch Changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ComponentDefinition } from "@kohaku-ui/registry";
|
|
2
|
+
import type { ComponentNode, JsonObject } from "@kohaku-ui/spec-core";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
import type { PartBuilder, RenderRuntime, Teardown } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* A part builder with typed props, taken from a `ComponentDefinition`'s `propsSchema` (design.md #68).
|
|
7
|
+
* Same shape as `PartBuilder`, with `props` added between `node` and `row`, so a product's builder never
|
|
8
|
+
* has to read `node.props["x"] as T` itself — the WC counterpart of renderer-react's typed `Component`.
|
|
9
|
+
*/
|
|
10
|
+
export type TypedPartBuilder<P> = (rt: RenderRuntime, parent: ParentNode, node: ComponentNode, props: P, row: JsonObject | null) => Teardown;
|
|
11
|
+
/** Registration triple returned by `implementWc`, passed to `<kohaku-surface>.registerPart`. */
|
|
12
|
+
export interface PartEntry {
|
|
13
|
+
type: string;
|
|
14
|
+
version: string;
|
|
15
|
+
builder: PartBuilder;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Wraps a typed `TypedPartBuilder` into a plain `PartBuilder` bound to `def`'s own type and version — the
|
|
19
|
+
* WC counterpart of renderer-react's `implement` (design.md #68). Usage:
|
|
20
|
+
* ```ts
|
|
21
|
+
* const entry = implementWc(myPartDef, (rt, parent, node, props, row) => { ... });
|
|
22
|
+
* surface.registerPart(entry.type, entry.version, entry.builder);
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* `def.propsSchema.safeParse` always runs, in every environment — not just outside a `NODE_ENV=production`
|
|
26
|
+
* build — because it is also what materializes a `.default()`-ed prop (or any other Zod transform) the Spec
|
|
27
|
+
* omits; skipping it in production would silently make `builder` see `undefined` for a prop its type says
|
|
28
|
+
* is always present. On success, `builder` receives `parsed.data`; on failure it receives the raw
|
|
29
|
+
* (unvalidated) `node.props` instead, matching the renderer's general fail-open policy. The parse (and so
|
|
30
|
+
* the warning) runs once per `node.props` object rather than on every rebuild. Only the **diagnostic** — a `console.warn` on a mismatch — is gated by environment: by default it fires outside a
|
|
31
|
+
* `NODE_ENV=production` build (see `isDevEnvironment`); pass `{ validate }` to force it on or off
|
|
32
|
+
* regardless of environment.
|
|
33
|
+
*/
|
|
34
|
+
export declare function implementWc<P extends z.ZodObject>(def: ComponentDefinition<P>, builder: TypedPartBuilder<z.infer<P>>, options?: {
|
|
35
|
+
validate?: boolean;
|
|
36
|
+
}): PartEntry;
|
|
37
|
+
//# sourceMappingURL=implement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement.d.ts","sourceRoot":"","sources":["../src/implement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAChC,EAAE,EAAE,aAAa,EACjB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,CAAC,EACR,GAAG,EAAE,UAAU,GAAG,IAAI,KACnB,QAAQ,CAAC;AAEd,gGAAgG;AAChG,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,EAC/C,GAAG,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EACrC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,SAAS,CAyBX"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isDevEnvironment } from "@kohaku-ui/renderer-core";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a typed `TypedPartBuilder` into a plain `PartBuilder` bound to `def`'s own type and version — the
|
|
4
|
+
* WC counterpart of renderer-react's `implement` (design.md #68). Usage:
|
|
5
|
+
* ```ts
|
|
6
|
+
* const entry = implementWc(myPartDef, (rt, parent, node, props, row) => { ... });
|
|
7
|
+
* surface.registerPart(entry.type, entry.version, entry.builder);
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* `def.propsSchema.safeParse` always runs, in every environment — not just outside a `NODE_ENV=production`
|
|
11
|
+
* build — because it is also what materializes a `.default()`-ed prop (or any other Zod transform) the Spec
|
|
12
|
+
* omits; skipping it in production would silently make `builder` see `undefined` for a prop its type says
|
|
13
|
+
* is always present. On success, `builder` receives `parsed.data`; on failure it receives the raw
|
|
14
|
+
* (unvalidated) `node.props` instead, matching the renderer's general fail-open policy. The parse (and so
|
|
15
|
+
* the warning) runs once per `node.props` object rather than on every rebuild. Only the **diagnostic** — a `console.warn` on a mismatch — is gated by environment: by default it fires outside a
|
|
16
|
+
* `NODE_ENV=production` build (see `isDevEnvironment`); pass `{ validate }` to force it on or off
|
|
17
|
+
* regardless of environment.
|
|
18
|
+
*/
|
|
19
|
+
export function implementWc(def, builder, options) {
|
|
20
|
+
const shouldWarn = options?.validate ?? isDevEnvironment();
|
|
21
|
+
// Parse once per props object, not once per rebuild: `node.props` keeps its identity for as long as the
|
|
22
|
+
// Spec does, so a rebuild (and the mismatch warning) reuses the first result. Keyed weakly, so a replaced
|
|
23
|
+
// Spec's props are collected with it.
|
|
24
|
+
const resolved = new WeakMap();
|
|
25
|
+
const wrapped = (rt, parent, node, row) => {
|
|
26
|
+
let props = resolved.get(node.props);
|
|
27
|
+
if (props === undefined) {
|
|
28
|
+
const parsed = def.propsSchema.safeParse(node.props);
|
|
29
|
+
if (parsed.success) {
|
|
30
|
+
props = parsed.data;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
props = node.props;
|
|
34
|
+
if (shouldWarn) {
|
|
35
|
+
console.warn(`[kohaku] component "${def.type}" (node ${node.id}) received props that don't match its schema: ${parsed.error.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
resolved.set(node.props, props);
|
|
39
|
+
}
|
|
40
|
+
return builder(rt, parent, node, props, row);
|
|
41
|
+
};
|
|
42
|
+
return { type: def.type, version: def.version, builder: wrapped };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=implement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"implement.js","sourceRoot":"","sources":["../src/implement.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAyB5D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,WAAW,CACzB,GAA2B,EAC3B,OAAqC,EACrC,OAAgC;IAEhC,MAAM,UAAU,GAAG,OAAO,EAAE,QAAQ,IAAI,gBAAgB,EAAE,CAAC;IAC3D,wGAAwG;IACxG,0GAA0G;IAC1G,sCAAsC;IACtC,MAAM,QAAQ,GAAG,IAAI,OAAO,EAAsB,CAAC;IACnD,MAAM,OAAO,GAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACrD,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,KAAK,GAAG,MAAM,CAAC,IAAkB,CAAC;YACpC,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,IAAI,CAAC,KAAmB,CAAC;gBACjC,IAAI,UAAU,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CACV,uBAAuB,GAAG,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,iDAAiD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CACzH,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACpE,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { DEFAULT_MESSAGES, type RendererMessages } from "@kohaku-ui/renderer-core";
|
|
1
|
+
export { type ActionManifest, type ActionManifestEntry, DEFAULT_MESSAGES, type PreflightActionResult, preflightAction, type RendererMessages, summarizeActionForModel, } from "@kohaku-ui/renderer-core";
|
|
2
|
+
export { implementWc, type PartEntry, type TypedPartBuilder } from "./implement.js";
|
|
2
3
|
export { defineKohakuSurface, KOHAKU_EVENT, KohakuSurface } from "./kohaku-surface.js";
|
|
3
4
|
export { createCoreRenderRegistry } from "./registry.js";
|
|
4
5
|
export type { ActionPhase, PartBuilder, RenderRuntime, SurfaceContext, SurfaceEvent, Teardown, } from "./types.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,eAAe,EACf,KAAK,gBAAgB,EACrB,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EACV,WAAW,EACX,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACZ,QAAQ,GACT,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
// A single <kohaku-surface> builds the entire UI Spec tree into one shadow root. The shared core is renderer-core.
|
|
3
3
|
// Dependency direction: spec-core → {registry, data-binding} → renderer-core → renderer-wc (sandbox is imported directly).
|
|
4
4
|
// UI message type and defaults (the single source of truth is renderer-core). Used to override context.messages.
|
|
5
|
-
|
|
5
|
+
// Governed actions (design.md #62/#63/#64): re-exported from renderer-core (the framework-free source of
|
|
6
|
+
// truth) so a consumer does not need to reach into @kohaku-ui/renderer-core directly for these.
|
|
7
|
+
export { DEFAULT_MESSAGES, preflightAction, summarizeActionForModel, } from "@kohaku-ui/renderer-core";
|
|
8
|
+
export { implementWc } from "./implement.js";
|
|
6
9
|
export { defineKohakuSurface, KOHAKU_EVENT, KohakuSurface } from "./kohaku-surface.js";
|
|
7
10
|
export { createCoreRenderRegistry } from "./registry.js";
|
|
8
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mHAAmH;AACnH,2HAA2H;AAE3H,iHAAiH;AACjH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,mHAAmH;AACnH,2HAA2H;AAE3H,iHAAiH;AACjH,yGAAyG;AACzG,gGAAgG;AAChG,OAAO,EAGL,gBAAgB,EAEhB,eAAe,EAEf,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAyC,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/kohaku-surface.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type RendererMessages } from "@kohaku-ui/renderer-core";
|
|
2
2
|
import { type ThemeTokens, type UISpec } from "@kohaku-ui/spec-core";
|
|
3
|
-
import type { SurfaceContext } from "./types.js";
|
|
3
|
+
import type { PartBuilder, SurfaceContext } from "./types.js";
|
|
4
4
|
/** Name of the forward event dispatched by <kohaku-surface> (detail is a SurfaceEvent). */
|
|
5
5
|
export declare const KOHAKU_EVENT = "kohaku-event";
|
|
6
|
+
/** `disclosure` attribute values (design.md #66); anything else (including absence) behaves as `"off"`. */
|
|
7
|
+
type DisclosureMode = "off" | "attributes" | "label";
|
|
6
8
|
/**
|
|
7
9
|
* The single host element <kohaku-surface> (Shadow DOM). It builds the entire UI Spec tree into one shadow root
|
|
8
10
|
* (rather than a Custom Element per part type — a single-element surface). Spec / context are received as properties.
|
|
@@ -15,8 +17,13 @@ export declare const KOHAKU_EVENT = "kohaku-event";
|
|
|
15
17
|
*/
|
|
16
18
|
export declare class KohakuSurface extends HTMLElement {
|
|
17
19
|
#private;
|
|
20
|
+
static get observedAttributes(): string[];
|
|
18
21
|
constructor();
|
|
19
22
|
connectedCallback(): void;
|
|
23
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
24
|
+
/** `"off"` (default) | `"attributes"` | `"label"` (design.md #66). Reflects the `disclosure` attribute. */
|
|
25
|
+
get disclosure(): DisclosureMode;
|
|
26
|
+
set disclosure(v: DisclosureMode);
|
|
20
27
|
disconnectedCallback(): void;
|
|
21
28
|
/** UI Spec. Setting it triggers resetForIntent → rebuild the tree + reattach the controller. */
|
|
22
29
|
get spec(): UISpec | null;
|
|
@@ -35,8 +42,38 @@ export declare class KohakuSurface extends HTMLElement {
|
|
|
35
42
|
set onEvent(v: SurfaceContext["onEvent"]);
|
|
36
43
|
set onNodeError(v: SurfaceContext["onNodeError"]);
|
|
37
44
|
set onActionResult(v: SurfaceContext["onActionResult"]);
|
|
45
|
+
set actionManifest(v: SurfaceContext["actionManifest"]);
|
|
46
|
+
set confirm(v: SurfaceContext["confirm"]);
|
|
47
|
+
set requestApproval(v: SurfaceContext["requestApproval"]);
|
|
38
48
|
set sandbox(v: SurfaceContext["sandbox"]);
|
|
49
|
+
/**
|
|
50
|
+
* Public registration API for product-specific parts (design.md #68). Previously `#registry` was private
|
|
51
|
+
* with no way for a host to add its own component types — a product could not register anything beyond
|
|
52
|
+
* the core catalog on the WC surface at all. Pair with `implementWc` for typed props:
|
|
53
|
+
* ```ts
|
|
54
|
+
* const entry = implementWc(myPartDef, builder);
|
|
55
|
+
* surface.registerPart(entry.type, entry.version, entry.builder);
|
|
56
|
+
* ```
|
|
57
|
+
* `version` is recorded for parity with renderer-react's `ImplRegistry.register`; unlike that registry,
|
|
58
|
+
* this surface does not (yet) feed a `SurfaceCapabilities` table into `@kohaku-ui/registry`'s
|
|
59
|
+
* `negotiate()` — no host wires WC capability negotiation today (AGENTS.md's renderer-wc section) — so
|
|
60
|
+
* `version` is not itself consulted by any negotiation path yet. Call before assigning `spec` when
|
|
61
|
+
* possible; if a Spec is already mounted, this rebuilds it so the newly registered part renders
|
|
62
|
+
* immediately instead of only taking effect on the next Spec swap.
|
|
63
|
+
*
|
|
64
|
+
* `#registry` starts seeded with the 16 core-catalog parts (`createCoreRenderRegistry`), so registering
|
|
65
|
+
* an already-taken `type` — most easily a core one, by typo or intentional override — silently replaces
|
|
66
|
+
* it. That is sometimes exactly what a product wants (replacing a core part with its own), but silent
|
|
67
|
+
* shadowing of a core part is also an easy way to lose one by accident, so replacing any existing entry
|
|
68
|
+
* warns via `console.warn` unless the caller opts in explicitly with `{ override: true }`.
|
|
69
|
+
*/
|
|
70
|
+
registerPart(type: string, version: string, builder: PartBuilder, options?: {
|
|
71
|
+
override?: boolean;
|
|
72
|
+
}): void;
|
|
73
|
+
/** The version last passed to `registerPart` for `type`, if any (introspection only — see its doc comment). */
|
|
74
|
+
getPartVersion(type: string): string | undefined;
|
|
39
75
|
}
|
|
40
76
|
/** Registers <kohaku-surface> as a custom element (idempotent). */
|
|
41
77
|
export declare function defineKohakuSurface(tagName?: string): void;
|
|
78
|
+
export {};
|
|
42
79
|
//# sourceMappingURL=kohaku-surface.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kohaku-surface.d.ts","sourceRoot":"","sources":["../src/kohaku-surface.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"kohaku-surface.d.ts","sourceRoot":"","sources":["../src/kohaku-surface.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,gBAAgB,EAItB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAIxF,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAY,MAAM,YAAY,CAAC;AAExE,2FAA2F;AAC3F,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAiC3C,2GAA2G;AAC3G,KAAK,cAAc,GAAG,KAAK,GAAG,YAAY,GAAG,OAAO,CAAC;AAErD;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,WAAW;;IAqB5C,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,cAYC;IAED,iBAAiB,IAAI,IAAI,CAWxB;IAED,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE7F;IAED,2GAA2G;IAC3G,IAAI,UAAU,IAAI,cAAc,CAG/B;IACD,IAAI,UAAU,CAAC,CAAC,EAAE,cAAc,EAG/B;IAED,oBAAoB,IAAI,IAAI,CAG3B;IAUD,gGAAgG;IAChG,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI,CAExB;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAK3B;IAED;;;OAGG;IACH,IAAI,OAAO,IAAI,cAAc,CAE5B;IACD,IAAI,OAAO,CAAC,IAAI,EAAE,cAAc,EAG/B;IAED,4EAA4E;IAC5E,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,EAEnC;IACD,IAAI,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAE/B;IACD,IAAI,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,SAAS,EAEpD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,WAAW,CAAC,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,EAE/C;IACD,IAAI,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAErD;IACD,IAAI,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAErD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IACD,IAAI,eAAe,CAAC,CAAC,EAAE,cAAc,CAAC,iBAAiB,CAAC,EAEvD;IACD,IAAI,OAAO,CAAC,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,EAEvC;IAoBD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAUxG;IAED,+GAA+G;IAC/G,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/C;CAuHF;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,OAAO,SAAmB,GAAG,IAAI,CAIpE"}
|
package/dist/kohaku-surface.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createDataInvalidationBus, createSpecStateStore, DEFAULT_LOCALE, DEFAULT_MESSAGES, PARTS_STATE_CSS, themeTokensToCssVars, } from "@kohaku-ui/renderer-core";
|
|
1
|
+
import { createDataInvalidationBus, createSpecStateStore, DEFAULT_LOCALE, DEFAULT_MESSAGES, deriveDisclosure, disclosureDomAttributes, PARTS_STATE_CSS, themeTokensToCssVars, } from "@kohaku-ui/renderer-core";
|
|
2
2
|
import { ROOT_COMPONENT_ID } from "@kohaku-ui/spec-core";
|
|
3
3
|
import { noop } from "./dom.js";
|
|
4
4
|
import { createCoreRenderRegistry } from "./registry.js";
|
|
@@ -9,9 +9,12 @@ export const KOHAKU_EVENT = "kohaku-event";
|
|
|
9
9
|
* Individual context properties whose change requires tearing down and rebuilding the mounted tree
|
|
10
10
|
* (they are baked into the RenderRuntime at #render time — binding/theme/locale/messages feed the
|
|
11
11
|
* BoundDataController / theme resolution / message lookup, and sandbox is read at mount time by the
|
|
12
|
-
* sandbox.html part builder). `onEvent` / `onNodeError` / `onActionResult`
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* sandbox.html part builder). `onEvent` / `onNodeError` / `onActionResult` and the action-governance
|
|
13
|
+
* hooks `actionManifest` / `confirm` / `requestApproval` are deliberately **not** in this set: they are
|
|
14
|
+
* read live from `#context` at call time (see `#dispatchForward` and the wrapper closures / getters built
|
|
15
|
+
* in `#render`), so reassigning one of them mid-lifecycle must not force a full rebuild -- which would
|
|
16
|
+
* tear down every mounted L2 sandbox iframe, and would fire twice when a host assigns the spec and its
|
|
17
|
+
* manifest back to back.
|
|
15
18
|
*/
|
|
16
19
|
const REBUILD_KEYS = new Set(["binding", "theme", "locale", "messages", "sandbox"]);
|
|
17
20
|
/** Every property <kohaku-surface> exposes as a plain instance accessor (used by #upgradeProperty). */
|
|
@@ -25,7 +28,11 @@ const UPGRADE_PROPS = [
|
|
|
25
28
|
"onEvent",
|
|
26
29
|
"onNodeError",
|
|
27
30
|
"onActionResult",
|
|
31
|
+
"actionManifest",
|
|
32
|
+
"confirm",
|
|
33
|
+
"requestApproval",
|
|
28
34
|
"sandbox",
|
|
35
|
+
"disclosure",
|
|
29
36
|
];
|
|
30
37
|
/**
|
|
31
38
|
* The single host element <kohaku-surface> (Shadow DOM). It builds the entire UI Spec tree into one shadow root
|
|
@@ -42,10 +49,22 @@ export class KohakuSurface extends HTMLElement {
|
|
|
42
49
|
#spec = null;
|
|
43
50
|
#context = {};
|
|
44
51
|
#registry = createCoreRenderRegistry();
|
|
52
|
+
// version is not read by #render (RenderRuntime.registry only needs the builder); it is recorded purely
|
|
53
|
+
// for parity with renderer-react's ImplRegistry.register (see registerPart's doc comment).
|
|
54
|
+
#partVersions = new Map();
|
|
45
55
|
// The store is retained by intent.hash; the bus stays the same for the surface's lifetime (equivalent to renderer-react's Provider useRef bus).
|
|
46
56
|
#store = null;
|
|
47
57
|
#bus = createDataInvalidationBus();
|
|
48
58
|
#teardown = noop;
|
|
59
|
+
// AI-generation disclosure (design.md #66). Unlike the properties above, `disclosure` is attribute-backed
|
|
60
|
+
// (`static observedAttributes` + `attributeChangedCallback`) rather than a plain instance accessor: it is
|
|
61
|
+
// a simple serializable enum, well suited to `<kohaku-surface disclosure="label">` markup, unlike
|
|
62
|
+
// spec/context/binding/theme which need real object identity. #disclosureLabelEl is the shadow-root
|
|
63
|
+
// element `#applyDisclosure` creates on demand for `disclosure="label"`; null in every other mode.
|
|
64
|
+
#disclosureLabelEl = null;
|
|
65
|
+
static get observedAttributes() {
|
|
66
|
+
return ["disclosure"];
|
|
67
|
+
}
|
|
49
68
|
constructor() {
|
|
50
69
|
super();
|
|
51
70
|
const shadow = this.attachShadow({ mode: "open" });
|
|
@@ -66,12 +85,28 @@ export class KohakuSurface extends HTMLElement {
|
|
|
66
85
|
// forever, so its setter logic (patchContext / re-render) would never fire again once the element is
|
|
67
86
|
// upgraded. Re-apply each such value through its accessor exactly once, here, at upgrade time
|
|
68
87
|
// (connectedCallback always fires after the class is defined). A no-op for an element created after
|
|
69
|
-
// define() (no own-property was ever set).
|
|
88
|
+
// define() (no own-property was ever set). "disclosure" is listed last so, if it was pre-assigned
|
|
89
|
+
// alongside "spec", #render() (triggered by spec's own upgrade) already sees the right value.
|
|
70
90
|
for (const prop of UPGRADE_PROPS)
|
|
71
91
|
this.#upgradeProperty(prop);
|
|
72
92
|
if (this.#spec != null)
|
|
73
93
|
this.#render();
|
|
74
94
|
}
|
|
95
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
96
|
+
if (name === "disclosure" && oldValue !== newValue)
|
|
97
|
+
this.#applyDisclosure();
|
|
98
|
+
}
|
|
99
|
+
/** `"off"` (default) | `"attributes"` | `"label"` (design.md #66). Reflects the `disclosure` attribute. */
|
|
100
|
+
get disclosure() {
|
|
101
|
+
const v = this.getAttribute("disclosure");
|
|
102
|
+
return v === "attributes" || v === "label" ? v : "off";
|
|
103
|
+
}
|
|
104
|
+
set disclosure(v) {
|
|
105
|
+
if (v === "off")
|
|
106
|
+
this.removeAttribute("disclosure");
|
|
107
|
+
else
|
|
108
|
+
this.setAttribute("disclosure", v);
|
|
109
|
+
}
|
|
75
110
|
disconnectedCallback() {
|
|
76
111
|
this.#teardown();
|
|
77
112
|
this.#teardown = noop;
|
|
@@ -129,6 +164,15 @@ export class KohakuSurface extends HTMLElement {
|
|
|
129
164
|
set onActionResult(v) {
|
|
130
165
|
this.#patchContext({ onActionResult: v });
|
|
131
166
|
}
|
|
167
|
+
set actionManifest(v) {
|
|
168
|
+
this.#patchContext({ actionManifest: v });
|
|
169
|
+
}
|
|
170
|
+
set confirm(v) {
|
|
171
|
+
this.#patchContext({ confirm: v });
|
|
172
|
+
}
|
|
173
|
+
set requestApproval(v) {
|
|
174
|
+
this.#patchContext({ requestApproval: v });
|
|
175
|
+
}
|
|
132
176
|
set sandbox(v) {
|
|
133
177
|
this.#patchContext({ sandbox: v });
|
|
134
178
|
}
|
|
@@ -152,14 +196,51 @@ export class KohakuSurface extends HTMLElement {
|
|
|
152
196
|
if (needsRebuild && this.#spec != null)
|
|
153
197
|
this.#render();
|
|
154
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Public registration API for product-specific parts (design.md #68). Previously `#registry` was private
|
|
201
|
+
* with no way for a host to add its own component types — a product could not register anything beyond
|
|
202
|
+
* the core catalog on the WC surface at all. Pair with `implementWc` for typed props:
|
|
203
|
+
* ```ts
|
|
204
|
+
* const entry = implementWc(myPartDef, builder);
|
|
205
|
+
* surface.registerPart(entry.type, entry.version, entry.builder);
|
|
206
|
+
* ```
|
|
207
|
+
* `version` is recorded for parity with renderer-react's `ImplRegistry.register`; unlike that registry,
|
|
208
|
+
* this surface does not (yet) feed a `SurfaceCapabilities` table into `@kohaku-ui/registry`'s
|
|
209
|
+
* `negotiate()` — no host wires WC capability negotiation today (AGENTS.md's renderer-wc section) — so
|
|
210
|
+
* `version` is not itself consulted by any negotiation path yet. Call before assigning `spec` when
|
|
211
|
+
* possible; if a Spec is already mounted, this rebuilds it so the newly registered part renders
|
|
212
|
+
* immediately instead of only taking effect on the next Spec swap.
|
|
213
|
+
*
|
|
214
|
+
* `#registry` starts seeded with the 16 core-catalog parts (`createCoreRenderRegistry`), so registering
|
|
215
|
+
* an already-taken `type` — most easily a core one, by typo or intentional override — silently replaces
|
|
216
|
+
* it. That is sometimes exactly what a product wants (replacing a core part with its own), but silent
|
|
217
|
+
* shadowing of a core part is also an easy way to lose one by accident, so replacing any existing entry
|
|
218
|
+
* warns via `console.warn` unless the caller opts in explicitly with `{ override: true }`.
|
|
219
|
+
*/
|
|
220
|
+
registerPart(type, version, builder, options) {
|
|
221
|
+
if (this.#registry.has(type) && options?.override !== true) {
|
|
222
|
+
console.warn(`[kohaku] registerPart("${type}", ...) replaces an existing part. ` +
|
|
223
|
+
"Pass { override: true } if that is intentional.");
|
|
224
|
+
}
|
|
225
|
+
this.#registry.set(type, builder);
|
|
226
|
+
this.#partVersions.set(type, version);
|
|
227
|
+
if (this.#spec != null)
|
|
228
|
+
this.#render();
|
|
229
|
+
}
|
|
230
|
+
/** The version last passed to `registerPart` for `type`, if any (introspection only — see its doc comment). */
|
|
231
|
+
getPartVersion(type) {
|
|
232
|
+
return this.#partVersions.get(type);
|
|
233
|
+
}
|
|
155
234
|
#render() {
|
|
156
235
|
// Tear down the previous tree (controller detach / unsubscribe / sandbox destroy / anchor removal).
|
|
157
236
|
this.#teardown();
|
|
158
237
|
this.#teardown = noop;
|
|
159
238
|
this.#root.replaceChildren();
|
|
160
239
|
const spec = this.#spec;
|
|
161
|
-
if (spec == null)
|
|
240
|
+
if (spec == null) {
|
|
241
|
+
this.#applyDisclosure();
|
|
162
242
|
return;
|
|
243
|
+
}
|
|
163
244
|
const theme = this.#context.theme ?? {};
|
|
164
245
|
const messages = { ...DEFAULT_MESSAGES, ...(this.#context.messages ?? {}) };
|
|
165
246
|
const locale = this.#context.locale ?? DEFAULT_LOCALE;
|
|
@@ -175,6 +256,7 @@ export class KohakuSurface extends HTMLElement {
|
|
|
175
256
|
for (const [name, value] of Object.entries(themeTokensToCssVars(theme))) {
|
|
176
257
|
this.style.setProperty(name, value);
|
|
177
258
|
}
|
|
259
|
+
const liveContext = () => this.#context;
|
|
178
260
|
const rt = createRuntime({
|
|
179
261
|
spec,
|
|
180
262
|
store: this.#store,
|
|
@@ -187,21 +269,77 @@ export class KohakuSurface extends HTMLElement {
|
|
|
187
269
|
// just one of these callbacks does not go through #render again, so RenderRuntime's own onNodeError/
|
|
188
270
|
// onActionResult fields (baked in once here) must forward through a stable indirection instead of
|
|
189
271
|
// holding a stale reference to whatever callback existed when the tree was last built.
|
|
272
|
+
// actionManifest / confirm / requestApproval are exposed as getters for the same reason: they are
|
|
273
|
+
// consulted per invoke, so a reassignment takes effect on the next action without a rebuild.
|
|
190
274
|
ctx: {
|
|
191
275
|
...this.#context,
|
|
192
276
|
onNodeError: (args) => this.#context.onNodeError?.(args),
|
|
193
277
|
onActionResult: (args) => this.#context.onActionResult?.(args),
|
|
278
|
+
get actionManifest() {
|
|
279
|
+
return liveContext().actionManifest;
|
|
280
|
+
},
|
|
281
|
+
get confirm() {
|
|
282
|
+
return liveContext().confirm;
|
|
283
|
+
},
|
|
284
|
+
get requestApproval() {
|
|
285
|
+
return liveContext().requestApproval;
|
|
286
|
+
},
|
|
194
287
|
},
|
|
195
288
|
registry: this.#registry,
|
|
196
289
|
dispatchForward: (event) => this.#dispatchForward(event),
|
|
197
290
|
});
|
|
198
291
|
this.#teardown = mountTree(rt, this.#root, ROOT_COMPONENT_ID);
|
|
292
|
+
this.#applyDisclosure();
|
|
199
293
|
}
|
|
200
294
|
#dispatchForward(event) {
|
|
201
295
|
// composed so it crosses the shadow boundary and reaches the host page. Flows to both paths regardless of whether a sink exists.
|
|
202
296
|
this.dispatchEvent(new CustomEvent(KOHAKU_EVENT, { detail: event, bubbles: true, composed: true }));
|
|
203
297
|
this.#context.onEvent?.(event);
|
|
204
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Applies (or clears) AI-generation disclosure (design.md #66): the `data-kohaku-disclosure` /
|
|
301
|
+
* `data-kohaku-tier` / `data-digital-source-type` attributes go on the *host* element itself (`this`),
|
|
302
|
+
* not inside the shadow root -- unlike renderer-react's SpecView, which has no host element of its own
|
|
303
|
+
* to mark and so wraps the tree in an internal `<div>` instead. The visible label (`disclosure="label"`
|
|
304
|
+
* only) is a shadow-root element, since that is what is actually displayed. Called from `#render()`
|
|
305
|
+
* (spec/context changes) and from `attributeChangedCallback` (the `disclosure` attribute changing on
|
|
306
|
+
* its own, with no other change to re-render for).
|
|
307
|
+
*/
|
|
308
|
+
#applyDisclosure() {
|
|
309
|
+
const mode = this.disclosure;
|
|
310
|
+
if (mode === "off" || this.#spec == null) {
|
|
311
|
+
this.removeAttribute("data-kohaku-disclosure");
|
|
312
|
+
this.removeAttribute("data-kohaku-tier");
|
|
313
|
+
this.removeAttribute("data-digital-source-type");
|
|
314
|
+
this.#disclosureLabelEl?.remove();
|
|
315
|
+
this.#disclosureLabelEl = null;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const disclosure = deriveDisclosure(this.#spec.provenance);
|
|
319
|
+
const attrs = disclosureDomAttributes(disclosure);
|
|
320
|
+
this.setAttribute("data-kohaku-disclosure", attrs["data-kohaku-disclosure"]);
|
|
321
|
+
this.setAttribute("data-kohaku-tier", attrs["data-kohaku-tier"]);
|
|
322
|
+
if (attrs["data-digital-source-type"] != null) {
|
|
323
|
+
this.setAttribute("data-digital-source-type", attrs["data-digital-source-type"]);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
this.removeAttribute("data-digital-source-type");
|
|
327
|
+
}
|
|
328
|
+
if (mode === "label" && disclosure.level !== "none") {
|
|
329
|
+
const messages = { ...DEFAULT_MESSAGES, ...(this.#context.messages ?? {}) };
|
|
330
|
+
const text = disclosure.level === "ai-generated" ? messages.disclosureAiGenerated : messages.disclosureAiReviewed;
|
|
331
|
+
if (this.#disclosureLabelEl == null) {
|
|
332
|
+
this.#disclosureLabelEl = document.createElement("div");
|
|
333
|
+
this.#disclosureLabelEl.setAttribute("data-kohaku-disclosure-label", "");
|
|
334
|
+
this.shadowRoot.insertBefore(this.#disclosureLabelEl, this.#root);
|
|
335
|
+
}
|
|
336
|
+
this.#disclosureLabelEl.textContent = text;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
this.#disclosureLabelEl?.remove();
|
|
340
|
+
this.#disclosureLabelEl = null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
205
343
|
}
|
|
206
344
|
/** Registers <kohaku-surface> as a custom element (idempotent). */
|
|
207
345
|
export function defineKohakuSurface(tagName = "kohaku-surface") {
|