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