@neta-art/cohub-cli 8.4.0 → 9.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 (84) hide show
  1. package/README.md +25 -2
  2. package/dist/avatar.js +3 -1
  3. package/dist/board-export.d.ts +22 -41
  4. package/dist/board-export.js +178 -115
  5. package/dist/board-fonts.d.ts +7 -0
  6. package/dist/board-fonts.js +10 -0
  7. package/dist/board-kernel/browserAll.js +2760 -0
  8. package/dist/board-kernel/getPo2TextureFromSource.js +13138 -0
  9. package/dist/board-kernel/init.js +867 -0
  10. package/dist/board-kernel/webworkerAll.js +3 -0
  11. package/dist/board-kernel.d.ts +2 -0
  12. package/dist/board-kernel.js +28812 -0
  13. package/dist/board-time.d.ts +3 -0
  14. package/dist/board-time.js +28 -0
  15. package/dist/commands/apps.js +2 -2
  16. package/dist/commands/auth.js +2 -2
  17. package/dist/commands/boards/examples.d.ts +8 -2
  18. package/dist/commands/boards/examples.js +79 -411
  19. package/dist/commands/boards.d.ts +4 -9
  20. package/dist/commands/boards.js +356 -286
  21. package/dist/commands/desktop.d.ts +1 -5
  22. package/dist/commands/desktop.js +10 -19
  23. package/dist/commands/generations.js +1 -1
  24. package/dist/commands/me.js +1 -1
  25. package/dist/commands/public.js +3 -3
  26. package/dist/commands/references.js +3 -0
  27. package/dist/commands/referrals.js +2 -2
  28. package/dist/commands/run.js +3 -3
  29. package/dist/commands/runtime.d.ts +1 -1
  30. package/dist/commands/runtime.js +12 -8
  31. package/dist/commands/search.js +15 -7
  32. package/dist/commands/space-activity.js +3 -5
  33. package/dist/commands/space-displays.d.ts +2 -0
  34. package/dist/commands/space-displays.js +347 -0
  35. package/dist/commands/space-files.d.ts +5 -0
  36. package/dist/commands/space-files.js +215 -0
  37. package/dist/commands/space-invitations.js +3 -3
  38. package/dist/commands/spaces.d.ts +3 -11
  39. package/dist/commands/spaces.js +146 -75
  40. package/dist/fonts/geist-latin-400-normal.woff2 +0 -0
  41. package/dist/fonts/geist-latin-500-normal.woff2 +0 -0
  42. package/dist/fonts/geist-latin-600-normal.woff2 +0 -0
  43. package/dist/fonts/geist-latin-700-normal.woff2 +0 -0
  44. package/dist/fonts/geist-mono-latin-500-normal.woff2 +0 -0
  45. package/dist/fonts/geist-mono-latin-600-normal.woff2 +0 -0
  46. package/dist/fonts/geist-mono-latin-700-normal.woff2 +0 -0
  47. package/dist/index.js +7 -4
  48. package/dist/runtime/launch.d.ts +2 -0
  49. package/dist/runtime/launch.js +35 -8
  50. package/dist/runtime/presentation.d.ts +1 -0
  51. package/dist/runtime/presentation.js +3 -2
  52. package/dist/runtime/supervisor.d.ts +1 -0
  53. package/dist/runtime/supervisor.js +2 -1
  54. package/dist/self-update.d.ts +1 -0
  55. package/dist/self-update.js +52 -16
  56. package/dist/space-files/limit.d.ts +2 -0
  57. package/dist/space-files/limit.js +16 -0
  58. package/dist/space-files/operands.d.ts +16 -0
  59. package/dist/space-files/operands.js +33 -0
  60. package/dist/space-files/transfer.d.ts +53 -0
  61. package/dist/space-files/transfer.js +296 -0
  62. package/dist/space-files/upload.d.ts +14 -0
  63. package/dist/space-files/upload.js +53 -0
  64. package/dist/space.d.ts +18 -16
  65. package/dist/space.js +86 -39
  66. package/dist/web.d.ts +1 -0
  67. package/dist/web.js +6 -0
  68. package/package.json +8 -4
  69. package/dist/commands/board-domain.d.ts +0 -2
  70. package/dist/commands/board-domain.js +0 -16
  71. package/dist/commands/boards/animation.d.ts +0 -2
  72. package/dist/commands/boards/animation.js +0 -171
  73. package/dist/commands/boards/appearance.d.ts +0 -2
  74. package/dist/commands/boards/appearance.js +0 -127
  75. package/dist/commands/boards/batch.d.ts +0 -4
  76. package/dist/commands/boards/batch.js +0 -62
  77. package/dist/commands/boards/connections.d.ts +0 -2
  78. package/dist/commands/boards/connections.js +0 -95
  79. package/dist/commands/boards/context.d.ts +0 -31
  80. package/dist/commands/boards/context.js +0 -71
  81. package/dist/commands/boards/items.d.ts +0 -2
  82. package/dist/commands/boards/items.js +0 -184
  83. package/dist/commands/boards/transactions.d.ts +0 -12
  84. package/dist/commands/boards/transactions.js +0 -69
@@ -0,0 +1,3 @@
1
+ export declare function parseBoardTime(value: string, name?: string): number;
2
+ export declare function parseBoardTimes(value: string, name?: string, limit?: number): number[];
3
+ export declare function formatBoardTime(ms: number): string;
@@ -0,0 +1,28 @@
1
+ const UNITS = { "": 1, ms: 1, s: 1000, m: 60_000 };
2
+ export function parseBoardTime(value, name = "time") {
3
+ const match = /^\s*(\d+(?:\.\d+)?|\.\d+)\s*(ms|s|m)?\s*$/i.exec(value);
4
+ if (!match?.[1])
5
+ throw new Error(`${name}: expected a time such as 1500, 500ms, 12.5s or 2m`);
6
+ return Math.round(Number(match[1]) * (UNITS[(match[2] ?? "").toLowerCase()] ?? 1) * 1000) / 1000;
7
+ }
8
+ export function parseBoardTimes(value, name = "--at", limit = 10_000) {
9
+ const parts = value.split(":");
10
+ if (parts.length === 1)
11
+ return [parseBoardTime(value, name)];
12
+ if (parts.length !== 3)
13
+ throw new Error(`${name}: expected a time or start:end:step`);
14
+ const [start, end, step] = parts.map((part) => parseBoardTime(part, name));
15
+ if (step <= 0)
16
+ throw new Error(`${name}: step must be greater than zero`);
17
+ if (end < start)
18
+ throw new Error(`${name}: end must not be before start`);
19
+ const count = Math.floor((end - start) / step + 1e-9) + 1;
20
+ if (count > limit)
21
+ throw new Error(`${name}: ${count} frames exceed the limit of ${limit}`);
22
+ return Array.from({ length: count }, (_, index) => Math.round((start + index * step) * 1000) / 1000);
23
+ }
24
+ export function formatBoardTime(ms) {
25
+ if (ms < 1000)
26
+ return `${ms}ms`;
27
+ return `${Number((ms / 1000).toFixed(3))}s`;
28
+ }
@@ -275,7 +275,7 @@ export function registerApps(program) {
275
275
  .description("List apps in the target space")
276
276
  .option("--json", "Output as JSON")
277
277
  .action(async (opts) => {
278
- const spaceId = await resolveSpace(appsCmd);
278
+ const spaceId = await resolveSpace(appsCmd, { home: true });
279
279
  const client = createClient();
280
280
  try {
281
281
  const result = await client.apps.listBySpace(spaceId);
@@ -421,7 +421,7 @@ export function registerApps(program) {
421
421
  const source = opts.source ? parseChoice(opts.source, "source", ["workspace", "local"]) : resolveDefaultAppSource();
422
422
  if (target.targetType === "port" && opts.source)
423
423
  return error("Invalid source", "--source applies only to --file and --dir.");
424
- const spaceId = await resolveSpace(appsCmd);
424
+ const spaceId = await resolveSpace(appsCmd, { home: true });
425
425
  const client = createClient();
426
426
  let { targetType, targetRef } = target;
427
427
  let sourceRef = null;
@@ -1,5 +1,5 @@
1
1
  import { authSource, loginWithDeviceFlow, readAuthSession, refreshAccessToken, requestDeviceCode, revokeAndClearAuthSession, verifyDeviceCode } from "../auth.js";
2
- import { clearDefaultSpaceCache } from "../space.js";
2
+ import { clearHomeSpaceCache } from "../space.js";
3
3
  import { createClient } from "../client.js";
4
4
  import { table, json as outJson, jsonRequested, ok, error, spinner, handleHttp } from "../output.js";
5
5
  export function registerAuth(program) {
@@ -68,7 +68,7 @@ export function registerAuth(program) {
68
68
  .description("Clear stored Logto session")
69
69
  .action(async () => {
70
70
  await revokeAndClearAuthSession();
71
- clearDefaultSpaceCache();
71
+ clearHomeSpaceCache();
72
72
  if (process.env.COHUB_EXECUTION_TOKEN?.trim()) {
73
73
  ok("Local session cleared. COHUB_EXECUTION_TOKEN is still set.");
74
74
  }
@@ -1,4 +1,10 @@
1
1
  import type { Command } from "commander";
2
- export declare const BOARD_EXAMPLE_KEYS: string[];
3
- export declare function boardExample(kind: string, type?: string): unknown;
2
+ /**
3
+ * Starter documents for `boards create -i` and patches for `boards apply -i`.
4
+ * Every example is a valid Board patch; `boards.test.ts` checks that.
5
+ */
6
+ export declare const BOARD_EXAMPLES: Record<string, {
7
+ description: string;
8
+ value: Record<string, unknown>;
9
+ }>;
4
10
  export declare function registerBoardExampleCommands(boards: Command): void;
@@ -1,429 +1,97 @@
1
1
  import { json } from "../../output.js";
2
- const templates = {
3
- "batch:basic": {
4
- commands: [
5
- { type: "board.patch", patch: { title: "Updated title" } },
6
- { type: "item.patch", itemId: "title", patch: { props: { text: "Updated text" } } },
7
- ],
8
- },
9
- "create": {
10
- items: [{
11
- id: "title",
12
- type: "text",
13
- position: { x: 120, y: 80 },
14
- size: { width: 320, height: 48 },
15
- rotation: 0,
16
- props: { text: "Launch plan", fontSize: 32 },
17
- style: { color: "brand" },
18
- }],
19
- connections: [],
20
- effects: [],
21
- compositions: [],
22
- },
23
- "create:workflow": {
24
- items: [
25
- {
26
- id: "request",
27
- type: "text",
28
- position: { x: 80, y: 190 },
29
- size: { width: 180, height: 48 },
30
- rotation: 0,
31
- props: { text: "Request", fontSize: 28 },
32
- style: { color: "brand" },
33
- },
34
- {
35
- id: "agent",
36
- type: "geo",
37
- position: { x: 360, y: 150 },
38
- size: { width: 240, height: 120 },
39
- rotation: 0,
40
- props: { shape: "rounded", text: "Agent" },
41
- style: { color: "blue", fillOpacity: 0.08 },
2
+ /**
3
+ * Starter documents for `boards create -i` and patches for `boards apply -i`.
4
+ * Every example is a valid Board patch; `boards.test.ts` checks that.
5
+ */
6
+ export const BOARD_EXAMPLES = {
7
+ basic: {
8
+ description: "A title, a note and a shape.",
9
+ value: {
10
+ board: { title: "Launch plan" },
11
+ items: {
12
+ title: { type: "text", position: { x: 80, y: 60 }, props: { text: "Launch plan", fontSize: 48, fontWeight: 700 } },
13
+ note: { type: "text", position: { x: 80, y: 140 }, props: { text: "Ship the beta to ten design partners, then widen.", fontSize: 20, width: 420 } },
14
+ goal: { type: "shape", position: { x: 600, y: 60 }, size: { width: 240, height: 140 }, style: { stroke: "green", fill: "green", fillOpacity: 0.12, radius: 16 }, props: { text: "Beta in March" } },
42
15
  },
43
- {
44
- id: "result",
45
- type: "geo",
46
- position: { x: 700, y: 150 },
47
- size: { width: 240, height: 120 },
48
- rotation: 0,
49
- props: { shape: "rounded", text: "Result" },
50
- style: { color: "green", fillOpacity: 0.08 },
51
- },
52
- ],
53
- connections: [
54
- { id: "request-agent", source: { itemId: "request" }, target: { itemId: "agent" }, relation: "triggers", label: "invoke" },
55
- { id: "agent-result", source: { itemId: "agent" }, target: { itemId: "result" }, relation: "produces", label: "output" },
56
- ],
57
- effects: [],
58
- compositions: [],
16
+ },
59
17
  },
60
- "create:media": {
61
- items: [
62
- {
63
- id: "hero",
64
- type: "image",
65
- position: { x: 80, y: 100 },
66
- size: { width: 640, height: 360 },
67
- rotation: 0,
68
- source: { kind: "space-file", path: "assets/hero.webp" },
69
- props: {},
70
- },
71
- {
72
- id: "demo-video",
73
- type: "video",
74
- position: { x: 760, y: 100 },
75
- size: { width: 480, height: 270 },
76
- rotation: 0,
77
- source: { kind: "space-file", path: "assets/demo.mp4" },
78
- props: {},
18
+ workflow: {
19
+ description: "Steps joined by bound arrows with relations.",
20
+ value: {
21
+ items: {
22
+ request: { type: "shape", position: { x: 80, y: 160 }, size: { width: 200, height: 110 }, style: { stroke: "brand" }, props: { geometry: "rounded", text: "Request" } },
23
+ agent: { type: "shape", position: { x: 400, y: 160 }, size: { width: 200, height: 110 }, style: { stroke: "blue", fill: "blue", fillOpacity: 0.08 }, props: { geometry: "rounded", text: "Agent" } },
24
+ result: { type: "shape", position: { x: 720, y: 160 }, size: { width: 200, height: 110 }, style: { stroke: "green", fill: "green", fillOpacity: 0.08 }, props: { geometry: "rounded", text: "Result" } },
25
+ "request-agent": { type: "arrow", props: { start: { item: "request" }, end: { item: "agent" }, relation: "triggers", label: "invoke" } },
26
+ "agent-result": { type: "arrow", props: { start: { item: "agent" }, end: { item: "result" }, relation: "produces", label: "output" } },
79
27
  },
80
- {
81
- id: "soundtrack",
82
- type: "audio",
83
- position: { x: 80, y: 500 },
84
- size: { width: 400, height: 96 },
85
- rotation: 0,
86
- source: { kind: "space-file", path: "assets/soundtrack.mp3" },
87
- props: {},
88
- },
89
- {
90
- id: "brief",
91
- type: "file",
92
- position: { x: 520, y: 500 },
93
- size: { width: 320, height: 180 },
94
- rotation: 0,
95
- source: { kind: "space-file", path: "docs/brief.md" },
96
- props: {},
97
- },
98
- ],
99
- connections: [],
100
- effects: [],
101
- compositions: [],
28
+ },
102
29
  },
103
- "create:animation": {
104
- items: [
105
- {
106
- id: "title",
107
- type: "text",
108
- position: { x: 120, y: 80 },
109
- size: { width: 320, height: 48 },
110
- rotation: 0,
111
- props: { text: "Launch", fontSize: 32 },
112
- style: { color: "brand" },
30
+ slides: {
31
+ description: "Frames as slides; children move and clip with their frame.",
32
+ value: {
33
+ items: {
34
+ s1: { type: "frame", position: { x: 0, y: 0 }, size: { width: 1600, height: 900 }, props: { label: "Intro" } },
35
+ "s1-title": { type: "text", parent: "s1", position: { x: 120, y: 120 }, props: { text: "Special relativity", fontSize: 96, fontWeight: 700 } },
36
+ "s1-sub": { type: "text", parent: "s1", position: { x: 120, y: 260 }, props: { text: "Nothing outruns light", fontSize: 40 } },
37
+ s2: { type: "frame", position: { x: 1800, y: 0 }, size: { width: 1600, height: 900 }, props: { label: "Time dilation" } },
38
+ "s2-title": { type: "text", parent: "s2", position: { x: 120, y: 120 }, props: { text: "Moving clocks run slow", fontSize: 72, fontWeight: 700 } },
113
39
  },
114
- {
115
- id: "stroke",
116
- type: "draw",
117
- rotation: 0,
118
- props: { points: [{ x: 2, y: 98, p: 0.5 }, { x: 90, y: 2, p: 0.5 }, { x: 178, y: 98, p: 0.5 }] },
119
- style: { color: "violet", strokeWidth: 4 },
120
- },
121
- ],
122
- connections: [],
123
- effects: [{
124
- id: "pulse-title",
125
- target: { type: "item", itemId: "title" },
126
- kind: "effects.pulse",
127
- kindVersion: 1,
128
- lifecycle: "when-visible",
129
- timeOrigin: "visible",
130
- seed: "pulse-title",
131
- params: { amount: 0.04, period: 1600 },
132
- }],
133
- compositions: [
134
- {
135
- id: "intro",
136
- name: "Intro",
137
- timeline: {
138
- duration: 800,
139
- tracks: [{
140
- id: "title-opacity",
141
- target: { type: "item", itemId: "title" },
142
- channel: "style.opacity",
143
- fill: "both",
144
- keyframes: [{ time: 0, value: 0 }, { time: 800, value: 1, easing: "ease-out-cubic" }],
145
- }],
146
- },
40
+ },
41
+ },
42
+ lesson: {
43
+ description: "An animated lesson: entrances, a drawn arrow, typed text and camera moves with pause markers.",
44
+ value: {
45
+ items: {
46
+ s1: { type: "frame", size: { width: 1600, height: 900 }, props: { label: "Length contraction" } },
47
+ title: { type: "text", parent: "s1", position: { x: 120, y: 100 }, props: { text: "Moving rulers get shorter", fontSize: 72, fontWeight: 700 } },
48
+ ruler: { type: "shape", parent: "s1", position: { x: 200, y: 420 }, size: { width: 800, height: 60 }, style: { stroke: "blue", fill: "blue", fillOpacity: 0.15 }, props: { text: "1 m" } },
49
+ caption: { type: "text", parent: "s1", position: { x: 200, y: 560 }, props: { text: "L = L₀ √(1 − v²/c²)", fontSize: 44, font: "serif", reveal: 1 } },
50
+ motion: { type: "arrow", parent: "s1", style: { stroke: "rose", strokeWidth: 4 }, props: { start: { x: 200, y: 360 }, end: { x: 1000, y: 360 }, label: "v → c" } },
51
+ glow: { type: "effect", parent: "s1", position: { x: 1050, y: 380 }, size: { width: 160, height: 160 }, style: { fill: "amber" }, props: { kind: "glow" } },
147
52
  },
148
- {
149
- id: "draw-stroke",
150
- name: "Draw stroke",
151
- timeline: {
152
- duration: 900,
153
- clips: [{
154
- id: "reveal-stroke",
155
- kind: "draw.reveal",
156
- kindVersion: 1,
157
- target: { type: "item", itemId: "stroke" },
158
- start: 0,
159
- duration: 900,
160
- easing: "ease-out-cubic",
161
- seed: "reveal-stroke",
162
- }],
53
+ animations: {
54
+ lecture: {
55
+ markers: [{ at: 1500, label: "Ruler", pause: true }, { at: 5000, label: "Formula", pause: true }],
56
+ tracks: {
57
+ "title-in": { target: "title", property: "opacity", composite: "add", keyframes: [{ at: 0, value: 0 }, { at: 600, value: 1, ease: "ease-out" }] },
58
+ "arrow-draw": { target: "motion", property: "style.trim", keyframes: [{ at: 800, value: 0 }, { at: 1800, value: 1, ease: "ease-in-out" }] },
59
+ "ruler-shrink": { target: "ruler", property: "scale", keyframes: [{ at: 2000, value: 1 }, { at: 4000, value: { x: 0.45, y: 1 }, ease: "ease-in-out" }] },
60
+ "caption-type": { target: "caption", property: "props.reveal", keyframes: [{ at: 4200, value: 0 }, { at: 6000, value: 1 }] },
61
+ "camera-focus": { target: "camera", property: "focus", keyframes: [{ at: 0, value: "s1" }, { at: 2000, value: "ruler" }, { at: 4500, value: "s1" }] },
62
+ },
163
63
  },
164
64
  },
165
- ],
166
- },
167
- "item:text": {
168
- id: "title",
169
- type: "text",
170
- position: { x: 120, y: 80 },
171
- size: { width: 320, height: 48 },
172
- rotation: 0,
173
- props: { text: "Launch plan", fontSize: 32 },
174
- style: { color: "brand" },
175
- },
176
- "item:image": {
177
- id: "hero",
178
- type: "image",
179
- position: { x: 120, y: 160 },
180
- size: { width: 640, height: 360 },
181
- rotation: 0,
182
- source: { kind: "space-file", path: "assets/hero.webp" },
183
- props: {},
184
- },
185
- "item:geo": {
186
- id: "goal",
187
- type: "geo",
188
- position: { x: 120, y: 160 },
189
- size: { width: 280, height: 140 },
190
- rotation: 0,
191
- props: { shape: "rounded", text: "Ship" },
192
- style: { color: "green", fillOpacity: 0.12 },
193
- },
194
- "item:frame": {
195
- id: "scene",
196
- type: "frame",
197
- position: { x: 80, y: 60 },
198
- size: { width: 960, height: 540 },
199
- rotation: 0,
200
- props: { label: "Scene 1" },
201
- style: { color: "neutral" },
202
- },
203
- "item:draw": {
204
- id: "stroke",
205
- type: "draw",
206
- rotation: 0,
207
- props: { points: [{ x: 102, y: 198, p: 0.5 }, { x: 190, y: 102, p: 0.5 }, { x: 278, y: 198, p: 0.5 }] },
208
- style: { color: "violet", strokeWidth: 4 },
209
- },
210
- "item:arrow": {
211
- id: "arrow",
212
- type: "arrow",
213
- rotation: 0,
214
- props: { start: { x: 116, y: 150 }, end: { x: 344, y: 150 }, bend: 0, arrowStart: false, arrowEnd: true, label: "next" },
215
- style: { color: "brand", strokeWidth: 2.5 },
216
- },
217
- "item:video": {
218
- id: "demo-video",
219
- type: "video",
220
- position: { x: 120, y: 160 },
221
- size: { width: 640, height: 360 },
222
- rotation: 0,
223
- source: { kind: "space-file", path: "assets/demo.mp4" },
224
- props: {},
225
- },
226
- "item:audio": {
227
- id: "soundtrack",
228
- type: "audio",
229
- position: { x: 120, y: 540 },
230
- size: { width: 480, height: 96 },
231
- rotation: 0,
232
- source: { kind: "space-file", path: "assets/soundtrack.mp3" },
233
- props: {},
234
- },
235
- "item:file": {
236
- id: "brief",
237
- type: "file",
238
- position: { x: 120, y: 160 },
239
- size: { width: 360, height: 220 },
240
- rotation: 0,
241
- source: { kind: "space-file", path: "docs/brief.md" },
242
- props: {},
243
- },
244
- "item:task": {
245
- id: "task",
246
- type: "task",
247
- position: { x: 120, y: 160 },
248
- size: { width: 420, height: 240 },
249
- rotation: 0,
250
- props: { taskRunId: "task-run-id", snapshot: { taskType: "generation", status: "running", title: "Generate concept", artifactCount: 0, artifacts: [] } },
251
- },
252
- "effect:pulse": {
253
- id: "pulse-title",
254
- target: { type: "item", itemId: "title" },
255
- kind: "effects.pulse",
256
- kindVersion: 1,
257
- lifecycle: "when-visible",
258
- timeOrigin: "visible",
259
- seed: "pulse-title",
260
- params: { amount: 0.04, period: 1600 },
261
- },
262
- "effect:float": {
263
- id: "float-hero",
264
- target: { type: "item", itemId: "hero" },
265
- kind: "effects.float",
266
- kindVersion: 1,
267
- lifecycle: "when-visible",
268
- timeOrigin: "visible",
269
- seed: "float-hero",
270
- params: { distance: 8, period: 2200 },
271
- },
272
- "effect:deal": {
273
- id: "deal-title",
274
- target: { type: "item", itemId: "title" },
275
- kind: "effects.deal",
276
- kindVersion: 1,
277
- lifecycle: "on-enter",
278
- timeOrigin: "activation",
279
- seed: "deal-title",
280
- params: {},
281
- },
282
- "motion:enter-deal": {
283
- enter: {
284
- kind: "effects.deal",
285
- kindVersion: 1,
286
- params: {},
287
- },
288
- },
289
- "composition:fade": {
290
- id: "intro",
291
- name: "Intro",
292
- timeline: {
293
- duration: 800,
294
- tracks: [{
295
- id: "title-opacity",
296
- target: { type: "item", itemId: "title" },
297
- channel: "style.opacity",
298
- fill: "both",
299
- keyframes: [
300
- { time: 0, value: 0 },
301
- { time: 800, value: 1, easing: "ease-out-cubic" },
302
- ],
303
- }],
304
- },
305
- },
306
- "composition:reveal": {
307
- id: "reveal",
308
- name: "Reveal",
309
- timeline: {
310
- duration: 600,
311
- clips: [{ id: "reveal-title", kind: "text.reveal", kindVersion: 1, target: { type: "item", itemId: "title" }, start: 0, duration: 600, fill: "both", easing: "ease-out-cubic", params: { mode: "words" }, seed: "reveal-title" }],
312
- },
313
- },
314
- "composition:motion-path": {
315
- id: "motion",
316
- name: "Motion path",
317
- timeline: {
318
- duration: 1200,
319
- clips: [{ id: "move-hero", kind: "motion.path", kindVersion: 1, target: { type: "item", itemId: "hero" }, start: 0, duration: 1200, fill: "both", easing: "ease-in-out-cubic", params: { points: [{ x: 0, y: 0 }, { x: 160, y: -40 }, { x: 320, y: 0 }], orient: true }, seed: "move-hero" }],
320
- },
321
- },
322
- "composition:draw-reveal": {
323
- id: "draw-stroke",
324
- name: "Draw stroke",
325
- timeline: {
326
- duration: 900,
327
- clips: [{ id: "reveal-stroke", kind: "draw.reveal", kindVersion: 1, target: { type: "item", itemId: "stroke" }, start: 0, duration: 900, easing: "ease-out-cubic", seed: "reveal-stroke" }],
328
- },
329
- },
330
- "composition:trail": {
331
- id: "move-with-trail",
332
- name: "Move with trail",
333
- timeline: {
334
- duration: 1200,
335
- clips: [
336
- { id: "move-hero", kind: "motion.path", kindVersion: 1, target: { type: "item", itemId: "hero" }, start: 0, duration: 1200, easing: "ease-in-out-cubic", fill: "both", params: { points: [{ x: 0, y: 0 }, { x: 320, y: 0 }] }, seed: "move-hero" },
337
- { id: "trail-hero", kind: "effects.trail", kindVersion: 1, target: { type: "item", itemId: "hero" }, start: 0, duration: 1200, params: { alpha: 0.48 }, seed: "trail-hero" },
338
- ],
339
- },
340
- },
341
- "composition:impact": {
342
- id: "impact",
343
- name: "Impact",
344
- timeline: {
345
- duration: 500,
346
- clips: [{ id: "impact-ring", kind: "effects.impact", kindVersion: 1, target: { type: "board" }, start: 0, duration: 500, layer: "front", params: { center: { x: 400, y: 300 }, radius: 120 }, seed: "impact-ring" }],
347
- },
348
- },
349
- "composition:flash": {
350
- id: "flash",
351
- name: "Flash",
352
- timeline: {
353
- duration: 300,
354
- clips: [{ id: "screen-flash", kind: "effects.flash", kindVersion: 1, target: { type: "board" }, start: 0, duration: 300, layer: "screen", params: { alpha: 0.3 }, seed: "screen-flash" }],
355
- },
356
- },
357
- "composition:particles": {
358
- id: "celebrate",
359
- name: "Celebrate",
360
- timeline: {
361
- duration: 1000,
362
- clips: [{ id: "particles", kind: "effects.particles", kindVersion: 1, target: { type: "board" }, start: 0, duration: 1000, layer: "front", params: { count: 420, bounds: { x: 0, y: 0, width: 800, height: 600 } }, seed: "particles" }],
363
- },
364
- playback: { endBehavior: "reset" },
365
- },
366
- "composition:camera-shake": {
367
- id: "shake",
368
- name: "Camera shake",
369
- timeline: {
370
- duration: 500,
371
- clips: [{ id: "shake-camera", kind: "camera.shake", kindVersion: 1, target: { type: "camera" }, start: 0, duration: 500, layer: "screen", params: { amount: 8, frequency: 28 }, seed: "shake-camera" }],
372
65
  },
373
66
  },
374
- "composition:camera-focus": {
375
- id: "tour",
376
- name: "Tour",
377
- timeline: {
378
- duration: 1000,
379
- clips: [{
380
- id: "focus-title",
381
- kind: "camera.focus",
382
- kindVersion: 1,
383
- target: { type: "camera" },
384
- start: 0,
385
- duration: 700,
386
- layer: "screen",
387
- fill: "forwards",
388
- easing: "ease-out-cubic",
389
- params: { focus: { type: "item", itemId: "title" }, fit: "contain", padding: 32 },
390
- seed: "focus-title",
391
- }],
67
+ sketch: {
68
+ description: "A code-drawn item; `src` is a module in the Space exporting draw(ctx, frame).",
69
+ value: {
70
+ items: {
71
+ wave: { type: "sketch", position: { x: 0, y: 0 }, size: { width: 640, height: 360 }, props: { src: "sketches/wave.js", params: { amplitude: 60 } } },
72
+ },
73
+ animations: {
74
+ loop: { duration: 4000, play: "always", tracks: {} },
75
+ },
392
76
  },
393
77
  },
394
78
  };
395
- export const BOARD_EXAMPLE_KEYS = Object.keys(templates);
396
- export function boardExample(kind, type) {
397
- const key = type ? `${kind}:${type}` : kind;
398
- const template = templates[key];
399
- if (!template)
400
- throw new Error(`Unknown Board example: ${key}`);
401
- return template;
402
- }
403
79
  export function registerBoardExampleCommands(boards) {
404
- boards.command("examples [kind] [type]")
405
- .description("Print editable semantic JSON templates")
406
- .option("--list", "List available examples")
80
+ boards.command("examples [name]")
81
+ .description("Print a starter Board document or list the examples")
407
82
  .addHelpText("after", `
408
- Kinds:
409
- create [workflow|media|animation]
410
- batch basic
411
- item text|image|video|audio|file|task|geo|frame|draw|arrow
412
- effect pulse|float|deal
413
- motion enter-deal
414
- composition fade|reveal|draw-reveal|motion-path|trail|impact|flash|particles|camera-shake|camera-focus
415
-
416
- Examples:
417
- cohub boards examples --list
418
- cohub boards examples create workflow > board.json
419
- cohub boards examples batch basic > changes.json
420
- cohub boards examples item text > item.json
421
- cohub boards examples composition fade > intro.json`)
422
- .action((kind, type, options) => {
423
- if (options.list)
424
- return json(BOARD_EXAMPLE_KEYS);
425
- if (!kind)
426
- throw new Error("Example kind is required. Use --list to see available examples.");
427
- json(boardExample(kind, type));
83
+ Examples are valid input for create and apply:
84
+ cohub boards examples lesson > lesson.json
85
+ cohub boards create lesson.board -i lesson.json`)
86
+ .action((name) => {
87
+ if (!name) {
88
+ for (const [key, example] of Object.entries(BOARD_EXAMPLES))
89
+ console.log(`${key.padEnd(10)} ${example.description}`);
90
+ return;
91
+ }
92
+ const example = BOARD_EXAMPLES[name];
93
+ if (!example)
94
+ throw new Error(`Unknown example ${name}; expected ${Object.keys(BOARD_EXAMPLES).join(", ")}`);
95
+ json(example.value);
428
96
  });
429
97
  }
@@ -1,17 +1,12 @@
1
1
  import type { Command } from "commander";
2
2
  import { parseBoardJsonObject } from "../board-command-support.js";
3
3
  export declare const parseJsonObject: typeof parseBoardJsonObject;
4
- export declare function parseViewport(value?: string): {
4
+ export declare function parseRect(value: string, name?: string): {
5
5
  x: number;
6
6
  y: number;
7
7
  width: number;
8
8
  height: number;
9
- } | undefined;
10
- export declare function showCreated(result: {
11
- board: {
12
- id: string;
13
- title: string;
14
- version: number;
15
- };
16
- }, path: string): void;
9
+ };
10
+ /** `frames/%04d.png` → `frames/0012.png`; a sequence needs a `%d` placeholder. */
11
+ export declare function framePath(pattern: string, index: number): string;
17
12
  export declare function registerBoards(program: Command): Command;