@neta-art/cohub-cli 3.12.0 → 5.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.
- package/README.md +23 -51
- package/dist/board-export.js +5 -5
- package/dist/commands/board-domain.js +4 -0
- package/dist/commands/boards/animation.js +65 -142
- package/dist/commands/boards/appearance.js +32 -29
- package/dist/commands/boards/context.d.ts +17 -1
- package/dist/commands/boards/context.js +38 -1
- package/dist/commands/boards/examples.d.ts +4 -0
- package/dist/commands/boards/examples.js +224 -0
- package/dist/commands/boards/items.d.ts +2 -0
- package/dist/commands/boards/items.js +140 -0
- package/dist/commands/boards/nodes.d.ts +1 -0
- package/dist/commands/boards/nodes.js +8 -68
- package/dist/commands/boards.d.ts +6 -11
- package/dist/commands/boards.js +46 -148
- package/dist/commands/cron-jobs.js +1 -1
- package/dist/commands/tasks.js +1 -1
- package/dist/commands/works.js +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { json } from "../../output.js";
|
|
2
|
+
const frame = { x: 120, y: 80, width: 320, height: 48, rotation: 0 };
|
|
3
|
+
const templates = {
|
|
4
|
+
"create": {
|
|
5
|
+
items: [{
|
|
6
|
+
id: "title",
|
|
7
|
+
type: "text",
|
|
8
|
+
frame,
|
|
9
|
+
props: { text: "Launch plan", fontSize: 32 },
|
|
10
|
+
style: { color: "brand" },
|
|
11
|
+
}],
|
|
12
|
+
connections: [],
|
|
13
|
+
effects: [],
|
|
14
|
+
compositions: [],
|
|
15
|
+
},
|
|
16
|
+
"item:text": {
|
|
17
|
+
id: "title",
|
|
18
|
+
type: "text",
|
|
19
|
+
frame,
|
|
20
|
+
props: { text: "Launch plan", fontSize: 32 },
|
|
21
|
+
style: { color: "brand" },
|
|
22
|
+
},
|
|
23
|
+
"item:image": {
|
|
24
|
+
id: "hero",
|
|
25
|
+
type: "image",
|
|
26
|
+
frame: { x: 120, y: 160, width: 640, height: 360, rotation: 0 },
|
|
27
|
+
source: { kind: "space-file", path: "assets/hero.webp" },
|
|
28
|
+
props: {},
|
|
29
|
+
},
|
|
30
|
+
"item:geo": {
|
|
31
|
+
id: "goal",
|
|
32
|
+
type: "geo",
|
|
33
|
+
frame: { x: 120, y: 160, width: 280, height: 140, rotation: 0 },
|
|
34
|
+
props: { shape: "rounded", text: "Ship" },
|
|
35
|
+
style: { color: "green", fillOpacity: 0.12 },
|
|
36
|
+
},
|
|
37
|
+
"item:frame": {
|
|
38
|
+
id: "scene",
|
|
39
|
+
type: "frame",
|
|
40
|
+
frame: { x: 80, y: 60, width: 960, height: 540, rotation: 0 },
|
|
41
|
+
props: { label: "Scene 1" },
|
|
42
|
+
style: { color: "neutral" },
|
|
43
|
+
},
|
|
44
|
+
"item:draw": {
|
|
45
|
+
id: "stroke",
|
|
46
|
+
type: "draw",
|
|
47
|
+
frame: { x: 100, y: 100, width: 180, height: 100, rotation: 0 },
|
|
48
|
+
props: { points: [{ x: 0, y: 80, p: 0.5 }, { x: 90, y: 10, p: 0.8 }, { x: 180, y: 70, p: 0.5 }] },
|
|
49
|
+
style: { color: "violet", strokeWidth: 4 },
|
|
50
|
+
},
|
|
51
|
+
"item:arrow": {
|
|
52
|
+
id: "arrow",
|
|
53
|
+
type: "arrow",
|
|
54
|
+
frame: { x: 100, y: 100, width: 260, height: 100, rotation: 0 },
|
|
55
|
+
props: { start: { x: 116, y: 150 }, end: { x: 344, y: 150 }, bend: 0, arrowStart: false, arrowEnd: true, label: "next" },
|
|
56
|
+
style: { color: "brand", strokeWidth: 2.5 },
|
|
57
|
+
},
|
|
58
|
+
"item:video": {
|
|
59
|
+
id: "demo-video",
|
|
60
|
+
type: "video",
|
|
61
|
+
frame: { x: 120, y: 160, width: 640, height: 360, rotation: 0 },
|
|
62
|
+
source: { kind: "space-file", path: "assets/demo.mp4" },
|
|
63
|
+
props: {},
|
|
64
|
+
},
|
|
65
|
+
"item:audio": {
|
|
66
|
+
id: "soundtrack",
|
|
67
|
+
type: "audio",
|
|
68
|
+
frame: { x: 120, y: 540, width: 480, height: 96, rotation: 0 },
|
|
69
|
+
source: { kind: "space-file", path: "assets/soundtrack.mp3" },
|
|
70
|
+
props: {},
|
|
71
|
+
},
|
|
72
|
+
"item:file": {
|
|
73
|
+
id: "brief",
|
|
74
|
+
type: "file",
|
|
75
|
+
frame: { x: 120, y: 160, width: 360, height: 220, rotation: 0 },
|
|
76
|
+
source: { kind: "space-file", path: "docs/brief.md" },
|
|
77
|
+
props: {},
|
|
78
|
+
},
|
|
79
|
+
"item:task": {
|
|
80
|
+
id: "task",
|
|
81
|
+
type: "task",
|
|
82
|
+
frame: { x: 120, y: 160, width: 420, height: 240, rotation: 0 },
|
|
83
|
+
props: { taskRunId: "task-run-id", snapshot: { taskType: "generation", status: "running", title: "Generate concept", artifactCount: 0, artifacts: [] } },
|
|
84
|
+
},
|
|
85
|
+
"effect:pulse": {
|
|
86
|
+
id: "pulse-title",
|
|
87
|
+
target: { type: "item", itemId: "title" },
|
|
88
|
+
kind: "effects.pulse",
|
|
89
|
+
kindVersion: 1,
|
|
90
|
+
enabled: true,
|
|
91
|
+
lifecycle: "when-visible",
|
|
92
|
+
timeOrigin: "visible",
|
|
93
|
+
layer: "front",
|
|
94
|
+
seed: "pulse-title",
|
|
95
|
+
params: { amount: 0.04, period: 1600 },
|
|
96
|
+
assetRefs: [],
|
|
97
|
+
metadata: {},
|
|
98
|
+
},
|
|
99
|
+
"effect:float": {
|
|
100
|
+
id: "float-hero",
|
|
101
|
+
target: { type: "item", itemId: "hero" },
|
|
102
|
+
kind: "effects.float",
|
|
103
|
+
kindVersion: 1,
|
|
104
|
+
enabled: true,
|
|
105
|
+
lifecycle: "when-visible",
|
|
106
|
+
timeOrigin: "visible",
|
|
107
|
+
layer: "front",
|
|
108
|
+
seed: "float-hero",
|
|
109
|
+
params: { distance: 8, period: 2200 },
|
|
110
|
+
assetRefs: [],
|
|
111
|
+
metadata: {},
|
|
112
|
+
},
|
|
113
|
+
"composition:fade": {
|
|
114
|
+
id: "intro",
|
|
115
|
+
name: "Intro",
|
|
116
|
+
timeline: {
|
|
117
|
+
duration: 800,
|
|
118
|
+
tracks: [{
|
|
119
|
+
id: "title-opacity",
|
|
120
|
+
target: { type: "item", itemId: "title" },
|
|
121
|
+
channel: "style.opacity",
|
|
122
|
+
channelVersion: 1,
|
|
123
|
+
interpolation: "linear",
|
|
124
|
+
fill: "both",
|
|
125
|
+
keyframes: [
|
|
126
|
+
{ time: 0, value: 0 },
|
|
127
|
+
{ time: 800, value: 1, easing: "ease-out-cubic" },
|
|
128
|
+
],
|
|
129
|
+
metadata: {},
|
|
130
|
+
}],
|
|
131
|
+
clips: [],
|
|
132
|
+
markers: [],
|
|
133
|
+
},
|
|
134
|
+
playback: { loop: false, endBehavior: "hold", reducedMotion: { mode: "base" } },
|
|
135
|
+
metadata: {},
|
|
136
|
+
},
|
|
137
|
+
"composition:reveal": {
|
|
138
|
+
id: "reveal",
|
|
139
|
+
name: "Reveal",
|
|
140
|
+
timeline: {
|
|
141
|
+
duration: 900,
|
|
142
|
+
tracks: [{ id: "title-opacity", target: { type: "item", itemId: "title" }, channel: "style.opacity", channelVersion: 1, interpolation: "linear", fill: "both", keyframes: [{ time: 0, value: 0 }, { time: 600, value: 1 }], metadata: {} }],
|
|
143
|
+
clips: [{ id: "reveal-title", kind: "text.reveal", kindVersion: 1, target: { type: "item", itemId: "title" }, start: 0, duration: 600, layer: "content", fill: "both", easing: "ease-out-cubic", params: { mode: "words" }, assetRefs: [], seed: "reveal-title", metadata: {} }],
|
|
144
|
+
markers: [],
|
|
145
|
+
},
|
|
146
|
+
playback: { loop: false, endBehavior: "hold", reducedMotion: { mode: "base" } },
|
|
147
|
+
metadata: {},
|
|
148
|
+
},
|
|
149
|
+
"composition:motion-path": {
|
|
150
|
+
id: "motion",
|
|
151
|
+
name: "Motion path",
|
|
152
|
+
timeline: {
|
|
153
|
+
duration: 1200,
|
|
154
|
+
tracks: [],
|
|
155
|
+
clips: [{ id: "move-hero", kind: "motion.path", kindVersion: 1, target: { type: "item", itemId: "hero" }, start: 0, duration: 1200, layer: "content", fill: "both", easing: "ease-in-out-cubic", params: { points: [{ x: 0, y: 0 }, { x: 160, y: -40 }, { x: 320, y: 0 }], orient: true }, assetRefs: [], seed: "move-hero", metadata: {} }],
|
|
156
|
+
markers: [],
|
|
157
|
+
},
|
|
158
|
+
playback: { loop: false, endBehavior: "hold", reducedMotion: { mode: "base" } },
|
|
159
|
+
metadata: {},
|
|
160
|
+
},
|
|
161
|
+
"composition:particles": {
|
|
162
|
+
id: "celebrate",
|
|
163
|
+
name: "Celebrate",
|
|
164
|
+
timeline: {
|
|
165
|
+
duration: 1000,
|
|
166
|
+
tracks: [],
|
|
167
|
+
clips: [{ id: "particles", kind: "effects.particles", kindVersion: 1, target: { type: "board" }, start: 0, duration: 1000, layer: "front", fill: "none", easing: "linear", params: { count: 420, bounds: { x: 0, y: 0, width: 800, height: 600 } }, assetRefs: [], seed: "particles", metadata: {} }],
|
|
168
|
+
markers: [],
|
|
169
|
+
},
|
|
170
|
+
playback: { loop: false, endBehavior: "reset", reducedMotion: { mode: "base" } },
|
|
171
|
+
metadata: {},
|
|
172
|
+
},
|
|
173
|
+
"composition:camera-focus": {
|
|
174
|
+
id: "tour",
|
|
175
|
+
name: "Tour",
|
|
176
|
+
timeline: {
|
|
177
|
+
duration: 1000,
|
|
178
|
+
tracks: [],
|
|
179
|
+
clips: [{
|
|
180
|
+
id: "focus-title",
|
|
181
|
+
kind: "camera.focus",
|
|
182
|
+
kindVersion: 1,
|
|
183
|
+
target: { type: "camera" },
|
|
184
|
+
start: 0,
|
|
185
|
+
duration: 700,
|
|
186
|
+
layer: "screen",
|
|
187
|
+
fill: "forwards",
|
|
188
|
+
easing: "ease-out-cubic",
|
|
189
|
+
params: { focus: { type: "item", itemId: "title" }, fit: "contain", padding: 32 },
|
|
190
|
+
assetRefs: [],
|
|
191
|
+
seed: "focus-title",
|
|
192
|
+
metadata: {},
|
|
193
|
+
}],
|
|
194
|
+
markers: [],
|
|
195
|
+
},
|
|
196
|
+
playback: { loop: false, endBehavior: "hold", reducedMotion: { mode: "base" } },
|
|
197
|
+
metadata: {},
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
export const BOARD_EXAMPLE_KEYS = Object.keys(templates);
|
|
201
|
+
export function boardExample(kind, type) {
|
|
202
|
+
const key = type ? `${kind}:${type}` : kind;
|
|
203
|
+
const template = templates[key];
|
|
204
|
+
if (!template)
|
|
205
|
+
throw new Error(`Unknown Board example: ${key}`);
|
|
206
|
+
return template;
|
|
207
|
+
}
|
|
208
|
+
export function registerBoardExampleCommands(boards) {
|
|
209
|
+
boards.command("examples <kind> [type]")
|
|
210
|
+
.description("Print editable semantic JSON templates")
|
|
211
|
+
.addHelpText("after", `
|
|
212
|
+
Kinds:
|
|
213
|
+
create
|
|
214
|
+
item text|image|video|audio|file|task|geo|frame|draw|arrow
|
|
215
|
+
effect pulse|float
|
|
216
|
+
composition fade|reveal|motion-path|particles|camera-focus
|
|
217
|
+
|
|
218
|
+
Examples:
|
|
219
|
+
cohub boards examples item text > item.json
|
|
220
|
+
cohub boards examples composition fade > intro.json`)
|
|
221
|
+
.action((kind, type) => {
|
|
222
|
+
json(boardExample(kind, type));
|
|
223
|
+
});
|
|
224
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { BoardAuthoringItemSchema, BoardItemPatchSchema, } from "@neta-art/cohub";
|
|
3
|
+
import { BOARD_DOMAIN_INPUT_MAX_BYTES, readBoardJsonObject, } from "../../board-command-support.js";
|
|
4
|
+
import { handleHttp, json, jsonRequested, ok, table } from "../../output.js";
|
|
5
|
+
import { resolvedBoard, withJson, } from "./context.js";
|
|
6
|
+
const baseVersion = (value, fallback) => {
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
return fallback;
|
|
9
|
+
const parsed = Number(value);
|
|
10
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
11
|
+
throw new Error("base version must be a non-negative integer");
|
|
12
|
+
}
|
|
13
|
+
return parsed;
|
|
14
|
+
};
|
|
15
|
+
async function execute(boards, target, command, options) {
|
|
16
|
+
const board = await resolvedBoard(boards, target);
|
|
17
|
+
const snapshot = await board.summary();
|
|
18
|
+
const mutation = {
|
|
19
|
+
mutationId: options.mutationId?.trim() || randomUUID(),
|
|
20
|
+
baseVersion: baseVersion(options.baseVersion, snapshot.board.version),
|
|
21
|
+
dryRun: Boolean(options.dryRun),
|
|
22
|
+
commands: [command],
|
|
23
|
+
};
|
|
24
|
+
// Server-side validation: schema, version, references, cascade rules — the
|
|
25
|
+
// same checks the real write would run, just without persisting anything.
|
|
26
|
+
const receipt = await board.mutateSemantic(mutation);
|
|
27
|
+
if (jsonRequested(options))
|
|
28
|
+
return json(receipt);
|
|
29
|
+
if (options.dryRun) {
|
|
30
|
+
ok(`Validated against Board version ${receipt.board.version}; no changes written`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
ok(`${receipt.replayed ? "Replayed" : "Applied"} mutation at Board version ${receipt.board.version}`);
|
|
34
|
+
}
|
|
35
|
+
function mutationOptions(command) {
|
|
36
|
+
return withJson(command)
|
|
37
|
+
.option("--base-version <version>", "Expected Board version; defaults to latest")
|
|
38
|
+
.option("--mutation-id <id>", "Stable id for safe retries")
|
|
39
|
+
.option("--dry-run", "Validate on the server (references, version, cascade) without writing");
|
|
40
|
+
}
|
|
41
|
+
export function registerBoardItemCommands(boards) {
|
|
42
|
+
const items = boards.command("items").description("Author Board items with semantic JSON");
|
|
43
|
+
withJson(items.command("list <board>").alias("ls").description("List semantic Board items"))
|
|
44
|
+
.action(async (target, options) => {
|
|
45
|
+
try {
|
|
46
|
+
const board = await resolvedBoard(boards, target);
|
|
47
|
+
const snapshot = await board.authoring();
|
|
48
|
+
const items = snapshot.items ?? [];
|
|
49
|
+
if (jsonRequested(options))
|
|
50
|
+
return json(items);
|
|
51
|
+
table(items.map((item) => ({
|
|
52
|
+
id: item.id,
|
|
53
|
+
type: item.type,
|
|
54
|
+
x: item.frame.x,
|
|
55
|
+
y: item.frame.y,
|
|
56
|
+
width: item.frame.width,
|
|
57
|
+
height: item.frame.height,
|
|
58
|
+
})), [
|
|
59
|
+
{ key: "id", label: "ID" },
|
|
60
|
+
{ key: "type", label: "TYPE" },
|
|
61
|
+
{ key: "x", label: "X" },
|
|
62
|
+
{ key: "y", label: "Y" },
|
|
63
|
+
{ key: "width", label: "WIDTH" },
|
|
64
|
+
{ key: "height", label: "HEIGHT" },
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
catch (cause) {
|
|
68
|
+
handleHttp(cause);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
mutationOptions(items.command("create <board>")
|
|
72
|
+
.description("Create one item")
|
|
73
|
+
.requiredOption("-i, --input <file>", "Board Item JSON; use - for stdin")
|
|
74
|
+
.addHelpText("after", `
|
|
75
|
+
Minimal text item:
|
|
76
|
+
{"id":"title","type":"text","frame":{"x":120,"y":80,"width":320,"height":48,"rotation":0},"props":{"text":"Launch plan","fontSize":32},"style":{"color":"brand"}}`))
|
|
77
|
+
.action(async (target, options) => {
|
|
78
|
+
try {
|
|
79
|
+
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
80
|
+
await execute(boards, target, {
|
|
81
|
+
type: "item.create",
|
|
82
|
+
item: BoardAuthoringItemSchema.parse(value),
|
|
83
|
+
}, options);
|
|
84
|
+
}
|
|
85
|
+
catch (cause) {
|
|
86
|
+
handleHttp(cause);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
mutationOptions(items.command("patch <board> <item-id>")
|
|
90
|
+
.description("Recursively merge item fields")
|
|
91
|
+
.requiredOption("-i, --input <file>", "Board Item patch JSON; use - for stdin")
|
|
92
|
+
.addHelpText("after", `
|
|
93
|
+
Objects merge recursively, arrays replace, and null clears optional fields.
|
|
94
|
+
Move and rename a text item without replacing its size or font:
|
|
95
|
+
{"frame":{"x":160,"y":120},"props":{"text":"Updated"}}`))
|
|
96
|
+
.action(async (target, itemId, options) => {
|
|
97
|
+
try {
|
|
98
|
+
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
99
|
+
await execute(boards, target, {
|
|
100
|
+
type: "item.patch",
|
|
101
|
+
itemId,
|
|
102
|
+
patch: BoardItemPatchSchema.parse(value),
|
|
103
|
+
}, options);
|
|
104
|
+
}
|
|
105
|
+
catch (cause) {
|
|
106
|
+
handleHttp(cause);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
mutationOptions(items.command("replace <board> <item-id>")
|
|
110
|
+
.description("Replace one complete item")
|
|
111
|
+
.requiredOption("-i, --input <file>", "Complete Board Item JSON; use - for stdin"))
|
|
112
|
+
.action(async (target, itemId, options) => {
|
|
113
|
+
try {
|
|
114
|
+
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
115
|
+
await execute(boards, target, {
|
|
116
|
+
type: "item.replace",
|
|
117
|
+
itemId,
|
|
118
|
+
item: BoardAuthoringItemSchema.parse(value),
|
|
119
|
+
}, options);
|
|
120
|
+
}
|
|
121
|
+
catch (cause) {
|
|
122
|
+
handleHttp(cause);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
mutationOptions(items.command("delete <board> <item-id>").alias("rm")
|
|
126
|
+
.description("Delete one item")
|
|
127
|
+
.option("--cascade", "Atomically remove relations, effects, and animation references"))
|
|
128
|
+
.action(async (target, itemId, options) => {
|
|
129
|
+
try {
|
|
130
|
+
await execute(boards, target, {
|
|
131
|
+
type: "item.delete",
|
|
132
|
+
itemId,
|
|
133
|
+
cascade: Boolean(options.cascade),
|
|
134
|
+
}, options);
|
|
135
|
+
}
|
|
136
|
+
catch (cause) {
|
|
137
|
+
handleHttp(cause);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -1,68 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createBoardConnection } from "@neta-art/cohub/board";
|
|
3
|
+
import { mutateSemantic } from "./context.js";
|
|
4
4
|
import { handleHttp } from "../../output.js";
|
|
5
5
|
import { resolvedBoard, showUpdated, withJson, } from "./context.js";
|
|
6
|
+
/** Connection shortcuts; Item authoring lives in ./items.ts. */
|
|
6
7
|
export function registerBoardNodeCommands(boards) {
|
|
7
|
-
const nodes = boards.command("nodes").description("Create and update Board nodes");
|
|
8
|
-
withJson(nodes.command("add <board>")
|
|
9
|
-
.description("Add a node")
|
|
10
|
-
.requiredOption("-i, --input <file>", "BoardNodeSpec JSON; use - for stdin")
|
|
11
|
-
.addHelpText("after", `
|
|
12
|
-
Frame x/y/width/height use Board world units. Draw points and arrow endpoints are also world input.
|
|
13
|
-
Minimal text node:
|
|
14
|
-
{"id":"title","type":"text","frame":{"x":120,"y":80,"width":320,"height":48},"text":"Launch plan"}`))
|
|
15
|
-
.action(async (target, options) => {
|
|
16
|
-
try {
|
|
17
|
-
const input = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
18
|
-
const node = createBoardNode(input);
|
|
19
|
-
const board = await resolvedBoard(boards, target);
|
|
20
|
-
showUpdated(await board.mutate({ build: () => [boardNodeCreateOperation(node)] }), options);
|
|
21
|
-
}
|
|
22
|
-
catch (cause) {
|
|
23
|
-
handleHttp(cause);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
withJson(nodes.command("patch <board> <node-id>")
|
|
27
|
-
.description("Patch a node")
|
|
28
|
-
.requiredOption("-i, --input <file>", "BoardNodeInput field patch; use - for stdin")
|
|
29
|
-
.addHelpText("after", `
|
|
30
|
-
x/y are absolute Board world coordinates.
|
|
31
|
-
Move a node without changing its content:
|
|
32
|
-
{"x":160,"y":120}
|
|
33
|
-
|
|
34
|
-
Nested view, style, and data fields replace their complete stored object.`))
|
|
35
|
-
.action(async (target, nodeId, options) => {
|
|
36
|
-
try {
|
|
37
|
-
const patch = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
38
|
-
if ("nodeId" in patch)
|
|
39
|
-
throw new Error("node patch must not contain nodeId");
|
|
40
|
-
const board = await resolvedBoard(boards, target);
|
|
41
|
-
showUpdated(await board.mutate({
|
|
42
|
-
build: () => [boardNodePatchOperation(nodeId, patch)],
|
|
43
|
-
}), options);
|
|
44
|
-
}
|
|
45
|
-
catch (cause) {
|
|
46
|
-
handleHttp(cause);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
withJson(nodes.command("remove <board> <node-id>")
|
|
50
|
-
.alias("rm")
|
|
51
|
-
.description("Remove a node and its connections"))
|
|
52
|
-
.action(async (target, nodeId, options) => {
|
|
53
|
-
try {
|
|
54
|
-
const board = await resolvedBoard(boards, target);
|
|
55
|
-
showUpdated(await board.mutate({
|
|
56
|
-
include: ["connections"],
|
|
57
|
-
build: (current) => boardNodeDeleteOperations(nodeId, current.connections),
|
|
58
|
-
}), options);
|
|
59
|
-
}
|
|
60
|
-
catch (cause) {
|
|
61
|
-
handleHttp(cause);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
8
|
withJson(boards.command("connect <board> <source> <target>")
|
|
65
|
-
.description("Connect two Board
|
|
9
|
+
.description("Connect two Board items")
|
|
66
10
|
.option("--id <id>", "Connection id")
|
|
67
11
|
.option("--relation <relation>", "Relation type")
|
|
68
12
|
.option("--direction <direction>", "none, forward, backward, or both", "forward")
|
|
@@ -78,17 +22,15 @@ Nested view, style, and data fields replace their complete stored object.`))
|
|
|
78
22
|
const board = await resolvedBoard(boards, target);
|
|
79
23
|
const connection = createBoardConnection({
|
|
80
24
|
id: options.id ?? randomUUID(),
|
|
81
|
-
|
|
82
|
-
|
|
25
|
+
sourceItemId: source,
|
|
26
|
+
targetItemId: destination,
|
|
83
27
|
relation: options.relation,
|
|
84
28
|
direction: direction,
|
|
85
29
|
label: options.label,
|
|
86
30
|
sourcePortId: options.sourcePort,
|
|
87
31
|
targetPortId: options.targetPort,
|
|
88
32
|
});
|
|
89
|
-
showUpdated(await board.
|
|
90
|
-
build: () => [{ type: "connection.create", payload: { connection } }],
|
|
91
|
-
}), options);
|
|
33
|
+
showUpdated(await mutateSemantic(board, [{ type: "connection.create", connection }]), options);
|
|
92
34
|
}
|
|
93
35
|
catch (cause) {
|
|
94
36
|
handleHttp(cause);
|
|
@@ -99,9 +41,7 @@ Nested view, style, and data fields replace their complete stored object.`))
|
|
|
99
41
|
.action(async (target, connectionId, options) => {
|
|
100
42
|
try {
|
|
101
43
|
const board = await resolvedBoard(boards, target);
|
|
102
|
-
showUpdated(await board.
|
|
103
|
-
build: () => [{ type: "connection.delete", payload: { connectionId } }],
|
|
104
|
-
}), options);
|
|
44
|
+
showUpdated(await mutateSemantic(board, [{ type: "connection.delete", connectionId }]), options);
|
|
105
45
|
}
|
|
106
46
|
catch (cause) {
|
|
107
47
|
handleHttp(cause);
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import type { BoardInspectInput, BoardTransactionInput } from "@neta-art/cohub";
|
|
2
1
|
import type { Command } from "commander";
|
|
3
2
|
import { parseBoardJsonObject } from "../board-command-support.js";
|
|
4
|
-
declare const INSPECT_SECTIONS: readonly ["nodes", "connections", "effects", "sequences", "clips", "playback"];
|
|
5
|
-
type InspectSection = (typeof INSPECT_SECTIONS)[number];
|
|
6
3
|
export declare const parseJsonObject: typeof parseBoardJsonObject;
|
|
7
|
-
export declare function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}): BoardTransactionInput;
|
|
4
|
+
export declare function parseViewport(value?: string): {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
} | undefined;
|
|
14
10
|
export declare function registerBoards(program: Command): Command;
|
|
15
|
-
export {};
|