@neta-art/cohub 5.9.0 → 6.0.0

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 (55) hide show
  1. package/README.md +42 -46
  2. package/dist/board/animation.d.ts +156 -67
  3. package/dist/board/animation.js +162 -277
  4. package/dist/board/codec.js +1 -1
  5. package/dist/board/export/index.d.ts +6 -0
  6. package/dist/board/export/index.js +6 -3
  7. package/dist/board/export/scene.d.ts +6 -0
  8. package/dist/board/export/scene.js +26 -2
  9. package/dist/board/geometry.d.ts +27 -1
  10. package/dist/board/geometry.js +92 -1
  11. package/dist/board/headless/index.d.ts +7 -1
  12. package/dist/board/headless/index.js +6 -2
  13. package/dist/board/index.d.ts +8 -6
  14. package/dist/board/index.js +7 -6
  15. package/dist/board/mutation.d.ts +15 -0
  16. package/dist/board/mutation.js +67 -0
  17. package/dist/board/render/css-color.d.ts +4 -0
  18. package/dist/board/render/css-color.js +36 -0
  19. package/dist/board/render/index.d.ts +2 -1
  20. package/dist/board/render/index.js +2 -1
  21. package/dist/board/render/renderers/base-card-renderer.js +3 -20
  22. package/dist/board/render/themes/clean-theme.js +6 -3
  23. package/dist/chunks/http.d.ts +30 -7
  24. package/dist/chunks/http.js +785 -289
  25. package/dist/chunks/websocket.d.ts +2675 -187
  26. package/dist/http.d.ts +3 -3
  27. package/dist/index.d.ts +158 -299
  28. package/dist/index.js +164 -279
  29. package/dist/protocol/dist/board-authoring.d.ts +1 -0
  30. package/dist/protocol/dist/board-authoring.js +217 -0
  31. package/dist/protocol/dist/board-capability-registry.d.ts +2 -0
  32. package/dist/protocol/dist/board-capability-registry.js +74 -0
  33. package/dist/protocol/dist/board-codec.d.ts +2 -0
  34. package/dist/protocol/dist/board-codec.js +4 -0
  35. package/dist/protocol/dist/board-composition.d.ts +258 -0
  36. package/dist/protocol/dist/board-composition.js +318 -0
  37. package/dist/protocol/dist/board-constants.d.ts +2 -1
  38. package/dist/protocol/dist/board-constants.js +40 -10
  39. package/dist/protocol/dist/board-document.d.ts +28 -2
  40. package/dist/protocol/dist/board-document.js +16 -3
  41. package/dist/protocol/dist/board-node.d.ts +1 -11
  42. package/dist/protocol/dist/board-node.js +1 -81
  43. package/dist/protocol/dist/board-upgrade.d.ts +1 -0
  44. package/dist/protocol/dist/board-upgrade.js +2 -0
  45. package/dist/protocol/dist/board-url.d.ts +2 -1
  46. package/dist/protocol/dist/board-url.js +8 -3
  47. package/dist/protocol/dist/board.d.ts +135 -76
  48. package/dist/protocol/dist/board.js +50 -69
  49. package/dist/protocol/dist/index.d.ts +9 -4
  50. package/dist/protocol/dist/index.js +9 -4
  51. package/dist/types.d.ts +3 -2
  52. package/docs/work-runtime-guide.md +19 -3
  53. package/package.json +3 -2
  54. package/dist/board/nodes.d.ts +0 -113
  55. package/dist/board/nodes.js +0 -154
package/README.md CHANGED
@@ -80,20 +80,16 @@ Use `space.boards` for collection operations and bind an ID with
80
80
  `space.board(boardId)` for entity operations:
81
81
 
82
82
  ```ts
83
- import { createBoardNode } from "@neta-art/cohub";
84
-
85
83
  const created = await space.boards.create({
86
84
  path: "boards/plan.board",
87
85
  title: "Plan",
88
- nodes: [
89
- createBoardNode({
90
- id: "goal",
91
- type: "geo",
92
- frame: { x: 80, y: 80, width: 240, height: 120 },
93
- text: "Ship",
94
- color: "green",
95
- }),
96
- ],
86
+ items: [{
87
+ id: "goal",
88
+ type: "geo",
89
+ frame: { x: 80, y: 80, width: 240, height: 120, rotation: 0 },
90
+ props: { shape: "rounded", text: "Ship" },
91
+ style: { color: "green" },
92
+ }],
97
93
  });
98
94
 
99
95
  const board = space.board(created.board.id);
@@ -102,22 +98,21 @@ const board = space.board(created.board.id);
102
98
  // Machine-readable types, enums and coordinate spaces for dynamic clients.
103
99
  const capabilities = await board.capabilities();
104
100
 
105
- const snapshot = await board.inspect({
106
- include: ["nodes", "effects", "sequences", "clips", "playback"],
107
- });
101
+ const snapshot = await board.authoring();
108
102
 
109
- await board.apply({
110
- txId: crypto.randomUUID(),
103
+ await board.mutateSemantic({
111
104
  baseVersion: snapshot.board.version,
112
- operations: [
113
- { type: "board.patch", payload: { patch: { title: "Updated plan" } } },
114
- ],
105
+ commands: [{
106
+ type: "item.patch",
107
+ itemId: "goal",
108
+ patch: { props: { text: "Updated plan" } },
109
+ }],
115
110
  });
116
111
 
117
112
  await board.play({
118
113
  commandId: crypto.randomUUID(),
119
114
  type: "play",
120
- sequenceId: "ambient",
115
+ compositionId: "ambient",
121
116
  });
122
117
  ```
123
118
 
@@ -156,40 +151,36 @@ graphics stack:
156
151
  ```ts
157
152
  import {
158
153
  BoardDocumentSchema,
159
- clip,
160
- compileSequence,
154
+ compileComposition,
161
155
  createBoardExtensionRegistry,
162
156
  itemBounds,
163
157
  planBoardExport,
164
- timeline,
165
158
  } from "@neta-art/cohub/board";
166
159
 
167
- const sequence = compileSequence({
160
+ const composition = compileComposition({
168
161
  id: "ambient",
169
162
  name: "Ambient",
170
- seed: "ambient-v1",
171
- timeline: clip({
172
- kind: "motion.keyframes",
173
- target: { type: "node", nodeId: "image" },
174
- duration: 1_000,
163
+ duration: 1_000,
164
+ tracks: [{
165
+ id: "image-translation",
166
+ target: { type: "item", itemId: "image" },
167
+ channel: "transform.translation",
168
+ fill: "both",
175
169
  keyframes: [
176
- { at: 0, value: { y: 0 } },
177
- { at: 500, value: { y: -8 } },
178
- { at: 1_000, value: { y: 0 } },
170
+ { time: 0, value: { x: 0, y: 0 } },
171
+ { time: 500, value: { x: 0, y: -8 } },
172
+ { time: 1_000, value: { x: 0, y: 0 } },
179
173
  ],
180
- }),
174
+ }],
175
+ playback: { loop: true, endBehavior: "hold", reducedMotion: { mode: "base" } },
181
176
  });
182
177
 
183
178
  await space.boards.create({
184
179
  path: "boards/ambient.board",
185
180
  metadata: {
186
- playback: {
187
- sequenceId: sequence.sequence.id,
188
- delayMs: 500,
189
- loop: true,
190
- },
181
+ playback: { compositionId: composition.id, delayMs: 500 },
191
182
  },
192
- sequences: [sequence],
183
+ compositions: [composition],
193
184
  });
194
185
  ```
195
186
 
@@ -241,8 +232,9 @@ access tokens — no API keys required.
241
232
 
242
233
  Four runtime-only APIs are available **exclusively inside a published Work**:
243
234
 
244
- - `client.context()` — returns Work identity, Space identity, and current
245
- permission scopes. Returns `null` outside a Work runtime.
235
+ - `client.context()` — returns Work identity, Space identity, the current
236
+ viewer, permission scopes, and optional UI Preview invocation identifiers.
237
+ Returns `null` outside a Work runtime.
246
238
  - `client.auth.request({ scopes, reason })` — shows the viewer a consent dialog
247
239
  and caches a token with the approved scopes.
248
240
  - `client.work.commerce.*` — entitlement checks, credit consumption,
@@ -259,6 +251,7 @@ const client = createCohubClient({ env: "prod" });
259
251
  const ctx = await client.context();
260
252
  if (!ctx?.space?.id) throw new Error("Not inside a published Work.");
261
253
 
254
+ const sourceSessionId = ctx.invocation?.sessionId ?? null;
262
255
  const space = client.space(ctx.space.id);
263
256
 
264
257
  // Request viewer scopes from a user gesture (button click)
@@ -274,11 +267,14 @@ Work permissions come in **two disjoint sets**:
274
267
 
275
268
  - **Work scopes** (read, no consent): `space.view`, `session.view`,
276
269
  `file.view`, `taskrun.view` — granted at publish time.
277
- - **Viewer scopes** (action, consent-required): `session.prompt.fullaccess`,
278
- `generation.create`, `user.space.list`, `user.session.list`,
279
- `user.usage.read` — approved per-viewer via `auth.request()`.
280
-
281
- > **Read operations need work scopes. Action operations need viewer scopes.
270
+ - **Viewer scopes** (consent-required): `taskrun.view`,
271
+ `session.prompt.fullaccess`, `generation.create`, `user.space.list`,
272
+ `user.session.list`, `user.usage.read` — approved per-viewer via
273
+ `auth.request()`.
274
+
275
+ Viewer-granted `taskrun.view` is checked against the viewer's own access to the
276
+ requested Space or Session. Other read operations need work scopes; action
277
+ operations need viewer scopes.
282
278
  They never substitute for each other.** For example, `session.prompt.fullaccess`
283
279
  lets you send a prompt but does NOT let you read the reply — that needs
284
280
  `session.view` (a work scope). Similarly, `generation.create` lets you create
@@ -1,38 +1,46 @@
1
1
  import { BoardCapability, BoardRenderCost } from "../protocol/dist/board-constants.js";
2
- import { BoardAssetRef, BoardClip, BoardDiagnostic, BoardEffect, BoardSequence, BoardTarget, BoardValidationResult } from "../protocol/dist/board.js";
2
+ import { BoardAnimationTarget, BoardComposition, BoardEasing, BoardProceduralClip, BoardTimelineMarker, BoardTrack, BoardTrackInterpolation } from "../protocol/dist/board-composition.js";
3
+ import { BoardAssetRef, BoardDiagnostic, BoardEffect, BoardValidationResult } from "../protocol/dist/board.js";
3
4
  import "../protocol/dist/index.js";
4
5
  //#region src/board/animation.d.ts
5
- type TimelineClipInput = Omit<BoardClip, "id" | "sequenceId" | "start" | "seed"> & {
6
- id?: string;
7
- seed?: string;
6
+ type CompositionInput = Omit<BoardComposition, "revision"> & {
7
+ revision?: number;
8
8
  };
9
- type TimelineInput = {
10
- type: "clip";
11
- clip: TimelineClipInput;
12
- } | {
13
- type: "parallel";
14
- children: TimelineInput[];
15
- } | {
16
- type: "sequence";
17
- children: TimelineInput[];
18
- } | {
19
- type: "stagger";
20
- each: number;
21
- children: TimelineInput[];
22
- } | {
23
- type: "delay";
24
- duration: number;
25
- child: TimelineInput;
26
- } | {
27
- type: "repeat";
28
- count: number;
29
- child: TimelineInput;
9
+ type TrackInput = Omit<BoardTrack, "channelVersion" | "metadata"> & {
10
+ channelVersion?: number;
11
+ metadata?: Record<string, unknown>;
30
12
  };
31
- type CompiledSequence = {
32
- sequence: Omit<BoardSequence, "boardId" | "revision">;
33
- clips: Array<Omit<BoardClip, "sequenceId">>;
34
- assetRefs: BoardAssetRef[];
13
+ type ProceduralClipInput = Omit<BoardProceduralClip, "kindVersion" | "layer" | "fill" | "easing" | "params" | "assetRefs" | "metadata"> & {
14
+ kindVersion?: number;
15
+ layer?: BoardProceduralClip["layer"];
16
+ fill?: BoardProceduralClip["fill"];
17
+ easing?: BoardEasing;
18
+ params?: Record<string, unknown>;
19
+ assetRefs?: BoardAssetRef[];
20
+ metadata?: Record<string, unknown>;
21
+ };
22
+ declare function track(input: TrackInput): BoardTrack;
23
+ declare function proceduralClip(input: ProceduralClipInput): BoardProceduralClip;
24
+ declare function composition(input: CompositionInput): BoardComposition;
25
+ declare function compileComposition(input: {
26
+ id: string;
27
+ name: string;
28
+ duration: number;
29
+ tracks?: TrackInput[];
30
+ clips?: ProceduralClipInput[];
31
+ markers?: BoardTimelineMarker[];
32
+ playback?: BoardComposition["playback"];
33
+ metadata?: Record<string, unknown>;
34
+ }): BoardComposition;
35
+ type SampledTrack = {
36
+ target: BoardAnimationTarget;
37
+ channel: string;
38
+ value: unknown;
35
39
  };
40
+ declare function sampleEasing(easing: BoardEasing, value: number): number;
41
+ /** Deterministically sample one validated Track without touching scene state. */
42
+ declare function sampleTrack(trackValue: BoardTrack, time: number): SampledTrack | null;
43
+ declare function sampleCompositionTracks(value: BoardComposition, time: number): SampledTrack[];
36
44
  type RenderBounds = {
37
45
  x: number;
38
46
  y: number;
@@ -48,54 +56,135 @@ type BoardExtensionDefinition = BoardCapability & {
48
56
  };
49
57
  type BoardPresetDefinition = BoardCapability & {
50
58
  kind: "preset";
51
- compile: (params: Record<string, unknown>) => TimelineInput;
59
+ compile: (params: Record<string, unknown>) => BoardComposition;
52
60
  };
53
61
  declare const DEFAULT_BOARD_LIMITS: BoardRenderCost;
54
- declare const timeline: {
55
- clip(clip: TimelineClipInput): TimelineInput;
56
- parallel(...children: TimelineInput[]): TimelineInput;
57
- sequence(...children: TimelineInput[]): TimelineInput;
58
- stagger(each: number, ...children: TimelineInput[]): TimelineInput;
59
- delay(duration: number, child: TimelineInput): TimelineInput;
60
- repeat(count: number, child: TimelineInput): TimelineInput;
61
- };
62
- declare function clip(input: {
63
- kind: string;
64
- target: BoardTarget;
65
- duration: number;
66
- params?: Record<string, unknown>;
67
- keyframes?: BoardClip["keyframes"];
68
- assetRefs?: BoardAssetRef[];
69
- easing?: string;
70
- fill?: BoardClip["fill"];
71
- layer?: BoardClip["layer"];
72
- kindVersion?: number;
73
- id?: string;
74
- seed?: string;
75
- metadata?: Record<string, unknown>;
76
- }): TimelineInput;
77
- declare function compileSequence(input: {
78
- id: string;
79
- name: string;
80
- seed: string;
81
- timeline: TimelineInput;
82
- restPose?: Record<string, unknown>;
83
- metadata?: Record<string, unknown>;
84
- }): CompiledSequence;
85
62
  declare class BoardExtensionRegistry {
86
63
  #private;
64
+ constructor();
87
65
  register(definition: BoardExtensionDefinition | BoardPresetDefinition): this;
88
66
  capabilities(): BoardCapability[];
89
- compilePreset(id: string, version: number, params: Record<string, unknown>): TimelineInput;
67
+ compilePreset(id: string, version: number, params: Record<string, unknown>): {
68
+ id: string;
69
+ name: string;
70
+ timeline: {
71
+ duration: number;
72
+ tracks: {
73
+ id: string;
74
+ target: {
75
+ type: "item";
76
+ itemId: string;
77
+ } | {
78
+ type: "effect";
79
+ effectId: string;
80
+ } | {
81
+ type: "camera";
82
+ } | {
83
+ type: "board";
84
+ };
85
+ channel: string;
86
+ channelVersion: number;
87
+ interpolation: "linear" | "step";
88
+ fill: "backwards" | "both" | "forwards" | "none";
89
+ keyframes: {
90
+ time: number;
91
+ value: unknown;
92
+ easing?: "ease-in-cubic" | "ease-in-out-cubic" | "ease-in-out-quad" | "ease-in-quad" | "ease-out-cubic" | "ease-out-expo" | "ease-out-quad" | "ease-out-quart" | "linear" | undefined;
93
+ }[];
94
+ metadata: Record<string, unknown>;
95
+ }[];
96
+ clips: {
97
+ id: string;
98
+ kind: string;
99
+ kindVersion: number;
100
+ target: {
101
+ type: "item";
102
+ itemId: string;
103
+ } | {
104
+ type: "effect";
105
+ effectId: string;
106
+ } | {
107
+ type: "camera";
108
+ } | {
109
+ type: "board";
110
+ };
111
+ start: number;
112
+ duration: number;
113
+ layer: "behind" | "content" | "front" | "screen";
114
+ fill: "backwards" | "both" | "forwards" | "none";
115
+ easing: "ease-in-cubic" | "ease-in-out-cubic" | "ease-in-out-quad" | "ease-in-quad" | "ease-out-cubic" | "ease-out-expo" | "ease-out-quad" | "ease-out-quart" | "linear";
116
+ params: Record<string, unknown>;
117
+ assetRefs: {
118
+ type: "extension" | "space-file";
119
+ ref: string;
120
+ digest?: string | undefined;
121
+ }[];
122
+ seed: string;
123
+ metadata: Record<string, unknown>;
124
+ }[];
125
+ markers: {
126
+ id: string;
127
+ time: number;
128
+ duration?: number | undefined;
129
+ metadata: Record<string, unknown>;
130
+ }[];
131
+ };
132
+ playback: {
133
+ loop: boolean;
134
+ endBehavior: "hold" | "reset";
135
+ reducedMotion: {
136
+ mode: "base";
137
+ } | {
138
+ mode: "time";
139
+ time: number;
140
+ } | {
141
+ mode: "marker";
142
+ markerId: string;
143
+ };
144
+ };
145
+ metadata: Record<string, unknown>;
146
+ revision: number;
147
+ };
90
148
  validate(input: {
91
- clips: Array<Omit<BoardClip, "sequenceId">>;
149
+ composition: BoardComposition;
92
150
  effects?: Array<Omit<BoardEffect, "boardId" | "revision">>;
93
151
  profile?: QualityProfile;
94
152
  limits?: BoardRenderCost;
95
153
  }): BoardValidationResult;
96
154
  }
97
- declare function createBoardExtensionRegistry(input?: {
98
- builtins?: boolean;
99
- }): BoardExtensionRegistry;
155
+ declare function createBoardExtensionRegistry(): BoardExtensionRegistry;
156
+ declare const BOARD_CHANNELS: {
157
+ readonly "transform.translation": {
158
+ readonly targets: readonly ["item"];
159
+ readonly value: import("zod").ZodObject<{
160
+ x: import("zod").ZodNumber;
161
+ y: import("zod").ZodNumber;
162
+ }, import("zod/v4/core").$strict>;
163
+ readonly interpolations: readonly ["linear", "step"];
164
+ readonly coordinateSpace: "world-offset";
165
+ readonly unit: "board";
166
+ };
167
+ readonly "transform.rotation": {
168
+ readonly targets: readonly ["item"];
169
+ readonly value: import("zod").ZodNumber;
170
+ readonly interpolations: readonly ["linear", "step"];
171
+ readonly unit: "radian";
172
+ };
173
+ readonly "transform.scale": {
174
+ readonly targets: readonly ["item"];
175
+ readonly value: import("zod").ZodUnion<readonly [import("zod").ZodNumber, import("zod").ZodObject<{
176
+ x: import("zod").ZodNumber;
177
+ y: import("zod").ZodNumber;
178
+ }, import("zod/v4/core").$strict>]>;
179
+ readonly interpolations: readonly ["linear", "step"];
180
+ readonly unit: "ratio";
181
+ };
182
+ readonly "style.opacity": {
183
+ readonly targets: readonly ["item"];
184
+ readonly value: import("zod").ZodNumber;
185
+ readonly interpolations: readonly ["linear", "step"];
186
+ readonly unit: "ratio";
187
+ };
188
+ };
100
189
  //#endregion
101
- export { BoardExtensionDefinition, BoardExtensionRegistry, BoardPresetDefinition, CompiledSequence, DEFAULT_BOARD_LIMITS, QualityProfile, RenderBounds, TimelineClipInput, TimelineInput, clip, compileSequence, createBoardExtensionRegistry, timeline };
190
+ export { BOARD_CHANNELS, BoardExtensionDefinition, BoardExtensionRegistry, BoardPresetDefinition, type BoardTrackInterpolation, CompositionInput, DEFAULT_BOARD_LIMITS, ProceduralClipInput, QualityProfile, RenderBounds, SampledTrack, TrackInput, compileComposition, composition, createBoardExtensionRegistry, proceduralClip, sampleCompositionTracks, sampleEasing, sampleTrack, track };