@neta-art/cohub 11.0.0 → 11.2.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 +31 -5
- package/dist/board/editor/context.d.ts +1 -0
- package/dist/board/editor/context.js +1 -0
- package/dist/board/editor/creation.d.ts +2 -2
- package/dist/board/editor/creation.js +3 -3
- package/dist/board/editor/editor.d.ts +5 -4
- package/dist/board/editor/editor.js +8 -1
- package/dist/board/editor/index.d.ts +2 -2
- package/dist/board/editor/interaction.d.ts +12 -2
- package/dist/board/editor/interaction.js +50 -8
- package/dist/board/editor/selection-transform.d.ts +1 -1
- package/dist/board/editor/selection-transform.js +13 -2
- package/dist/board/editor/snapshots.d.ts +0 -2
- package/dist/board/editor/snapshots.js +4 -20
- package/dist/board/export/index.d.ts +2 -0
- package/dist/board/export/index.js +1 -0
- package/dist/board/export/scene.d.ts +2 -0
- package/dist/board/export/scene.js +8 -1
- package/dist/board/model/file-preview.d.ts +0 -1
- package/dist/board/model/file-preview.js +10 -1
- package/dist/board/model/image-key.js +1 -1
- package/dist/board/model/index.d.ts +8 -7
- package/dist/board/model/index.js +6 -5
- package/dist/board/model/items/create.d.ts +16 -3
- package/dist/board/model/items/create.js +125 -5
- package/dist/board/model/items/index.d.ts +2 -2
- package/dist/board/model/items/index.js +2 -2
- package/dist/board/model/media-playback.js +1 -1
- package/dist/board/model/overlay-placement.d.ts +25 -0
- package/dist/board/model/overlay-placement.js +35 -0
- package/dist/board/model/replay.d.ts +1 -1
- package/dist/board/model/replay.js +1 -0
- package/dist/board/model/script-hit.d.ts +20 -3
- package/dist/board/model/script-hit.js +81 -24
- package/dist/board/model/script-observers.d.ts +18 -0
- package/dist/board/model/script-observers.js +92 -0
- package/dist/board/model/script-scene.d.ts +1 -1
- package/dist/board/model/script-scene.js +35 -8
- package/dist/board/model/script.d.ts +51 -7
- package/dist/board/render/audio-waveform.d.ts +1 -1
- package/dist/board/render/audio-waveform.js +8 -5
- package/dist/board/render/board-background.js +5 -4
- package/dist/board/render/card-tag.d.ts +24 -0
- package/dist/board/render/card-tag.js +205 -0
- package/dist/board/render/index.d.ts +8 -5
- package/dist/board/render/index.js +8 -5
- package/dist/board/render/media-interaction.d.ts +2 -1
- package/dist/board/render/media-interaction.js +15 -7
- package/dist/board/render/palette.js +2 -2
- package/dist/board/render/renderers/arrow-card-renderer.js +20 -16
- package/dist/board/render/renderers/audio-card-renderer.d.ts +8 -1
- package/dist/board/render/renderers/audio-card-renderer.js +64 -60
- package/dist/board/render/renderers/base-card-renderer.d.ts +1 -26
- package/dist/board/render/renderers/base-card-renderer.js +6 -92
- package/dist/board/render/renderers/board-renderer-registry.d.ts +25 -2
- package/dist/board/render/renderers/board-renderer-registry.js +11 -4
- package/dist/board/render/renderers/custom-card-renderer.js +6 -8
- package/dist/board/render/renderers/effect-card-renderer.js +0 -5
- package/dist/board/render/renderers/file-card-renderer.d.ts +1 -11
- package/dist/board/render/renderers/file-card-renderer.js +43 -105
- package/dist/board/render/renderers/frame-card-renderer.js +14 -15
- package/dist/board/render/renderers/image-card-renderer.js +62 -67
- package/dist/board/render/renderers/media-key.d.ts +6 -0
- package/dist/board/render/renderers/media-key.js +39 -0
- package/dist/board/render/renderers/shape-card-renderer.js +6 -12
- package/dist/board/render/renderers/task-card-renderer.d.ts +1 -8
- package/dist/board/render/renderers/task-card-renderer.js +286 -111
- package/dist/board/render/renderers/unknown-card-renderer.js +17 -16
- package/dist/board/render/renderers/video-card-renderer.js +73 -80
- package/dist/board/render/script-host.d.ts +1 -1
- package/dist/board/render/script-host.js +53 -28
- package/dist/board/render/script-runtime.js +13 -3
- package/dist/board/render/script-texture.d.ts +11 -0
- package/dist/board/render/script-texture.js +33 -0
- package/dist/board/render/stroke.d.ts +1 -1
- package/dist/board/render/stroke.js +5 -4
- package/dist/board/render/style.d.ts +29 -0
- package/dist/board/render/style.js +54 -0
- package/dist/board/render/surface.d.ts +34 -0
- package/dist/board/render/surface.js +132 -0
- package/dist/board/render/text-fit.d.ts +8 -0
- package/dist/board/render/text-fit.js +60 -0
- package/dist/board/render/text-resolution.d.ts +2 -1
- package/dist/board/render/text-resolution.js +4 -1
- package/dist/board/stage/assets.js +1 -1
- package/dist/board/stage/index.d.ts +3 -2
- package/dist/board/stage/index.js +2 -1
- package/dist/board/stage/overlay.d.ts +41 -0
- package/dist/board/stage/overlay.js +182 -0
- package/dist/board/stage/scene.d.ts +4 -43
- package/dist/board/stage/scene.js +38 -130
- package/dist/board/stage/script-pointer.d.ts +1 -0
- package/dist/board/stage/script-pointer.js +8 -2
- package/dist/board/stage/stage.d.ts +13 -3
- package/dist/board/stage/stage.js +217 -53
- package/dist/chunks/environment.d.ts +1037 -8
- package/dist/chunks/environment.js +12 -4
- package/dist/chunks/http.d.ts +51 -2
- package/dist/chunks/http.js +10 -1
- package/dist/chunks/transport.js +1 -1
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +151 -139
- package/dist/protocol/dist/board-model.d.ts +36 -1
- package/dist/protocol/dist/board-model.js +14 -4
- package/dist/protocol/dist/board-task.d.ts +22 -0
- package/dist/{board/model/task.js → protocol/dist/board-task.js} +34 -7
- package/dist/protocol/dist/generation/blocks.d.ts +8 -0
- package/dist/{generation-blocks.js → protocol/dist/generation/blocks.js} +14 -15
- package/dist/protocol/dist/index.d.ts +5 -2
- package/dist/protocol/dist/task/index.d.ts +4 -0
- package/package.json +2 -1
- package/dist/board/model/task.d.ts +0 -11
- package/dist/types.d.ts +0 -36
package/README.md
CHANGED
|
@@ -46,18 +46,39 @@ Development uses:
|
|
|
46
46
|
- API: `https://api-dev.cohub.live`
|
|
47
47
|
- WebSocket: `wss://gateway-dev.cohub.live/ws`
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Override endpoints for self-hosted deployments or proxies:
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
52
|
const client = createCohubClient({
|
|
53
53
|
baseUrl: "https://api.example.com",
|
|
54
|
+
webBaseUrl: "https://cohub.example.com",
|
|
54
55
|
getAccessToken: async () => localStorage.getItem("token"),
|
|
55
56
|
websocket: {
|
|
56
57
|
url: "https://gateway.example.com",
|
|
57
58
|
},
|
|
59
|
+
voice: {
|
|
60
|
+
url: "https://gateway.example.com",
|
|
61
|
+
},
|
|
58
62
|
});
|
|
59
63
|
```
|
|
60
64
|
|
|
65
|
+
In Node.js, the SDK and CLI share these optional environment variables:
|
|
66
|
+
|
|
67
|
+
| Variable | Endpoint |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `COHUB_BASE_URL` | API base URL |
|
|
70
|
+
| `COHUB_WS_URL` | Realtime gateway URL (`http(s)` or `ws(s)`; `/ws` is added when omitted) |
|
|
71
|
+
| `COHUB_WEB_URL` | Web origin for App authorization and CLI links |
|
|
72
|
+
| `COHUB_VOICE_WS_URL` | Voice input gateway URL; falls back to `COHUB_WS_URL` with `/asr/ws` |
|
|
73
|
+
|
|
74
|
+
Explicit client options take precedence over environment variables. Unconfigured
|
|
75
|
+
endpoints use the selected environment's defaults. API, web, and gateway addresses
|
|
76
|
+
are configured separately; changing `baseUrl` does not change the gateway.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
COHUB_BASE_URL=http://localhost:3000 COHUB_WS_URL=ws://localhost:8080 node app.js
|
|
80
|
+
```
|
|
81
|
+
|
|
61
82
|
## Spaces and sessions
|
|
62
83
|
|
|
63
84
|
A **Space** is a live, isolated working environment where users and agents create together.
|
|
@@ -101,10 +122,15 @@ const stop = board.subscribe({
|
|
|
101
122
|
});
|
|
102
123
|
```
|
|
103
124
|
|
|
104
|
-
Task Items
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
125
|
+
Task Items show a run by its `taskRunId`. Write only the id; the server fills
|
|
126
|
+
the display snapshot from the run and rewrites it when the run finishes, on
|
|
127
|
+
every Board in the Space that shows it. Lay a run's results out as media with
|
|
128
|
+
`createTaskResultItems`; each result keeps an `origin` leading back to the run.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const { taskRunId } = await client.generations.create({ spaceId, model, content });
|
|
132
|
+
await board.apply({ items: { hero: { type: "task", position: { x: 80, y: 80 }, size: { width: 480, height: 270 }, props: { taskRunId } } } });
|
|
133
|
+
```
|
|
108
134
|
|
|
109
135
|
### Layers
|
|
110
136
|
|
|
@@ -28,8 +28,8 @@ declare function createCreationModule(ctx: EditorContext): {
|
|
|
28
28
|
snapshot?: BoardMediaSnapshot;
|
|
29
29
|
}) => string;
|
|
30
30
|
addFile: (path: string, at: WorldPoint, snapshot?: BoardMediaSnapshot & BoardFileSnapshotFacts) => string;
|
|
31
|
-
addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown
|
|
32
|
-
addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown
|
|
31
|
+
addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown>, aspect?: number) => string;
|
|
32
|
+
addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown>, aspect?: number) => string | null;
|
|
33
33
|
addText: (text: string, at: WorldPoint) => void;
|
|
34
34
|
addShape: (at: WorldPoint, geometry?: ShapeKind) => void;
|
|
35
35
|
addFrame: (at: WorldPoint) => void;
|
|
@@ -53,10 +53,10 @@ function createCreationModule(ctx) {
|
|
|
53
53
|
if (options.readonly) return null;
|
|
54
54
|
return addOne(ctx.registry.create(type, input));
|
|
55
55
|
}
|
|
56
|
-
function addTaskWithSources(taskRunId, snapshot, at, sources, metadata) {
|
|
56
|
+
function addTaskWithSources(taskRunId, snapshot, at, sources, metadata, aspect) {
|
|
57
57
|
if (options.readonly) return null;
|
|
58
58
|
const { arrow } = state.toolStyles;
|
|
59
|
-
const task = createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata);
|
|
59
|
+
const task = createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata, void 0, aspect);
|
|
60
60
|
add([task, ...sources.filter((source) => source.itemId !== task.id && state.scene.get(source.itemId)).map((source) => {
|
|
61
61
|
const entry = createArrowBoardItem({
|
|
62
62
|
x: 0,
|
|
@@ -199,7 +199,7 @@ function createCreationModule(ctx) {
|
|
|
199
199
|
addApp: (app, at) => addOne(createAppBoardItem(app, at.x, at.y)),
|
|
200
200
|
addMedia: (src, at, options = {}) => addOne(createMediaBoardItem(src, at.x, at.y, options.snapshot, void 0, options.kind)),
|
|
201
201
|
addFile: (path, at, snapshot) => addOne(createFileNodeForPath(path, at.x, at.y, snapshot)),
|
|
202
|
-
addTask: (taskRunId, snapshot, at, metadata) => addOne(createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata)),
|
|
202
|
+
addTask: (taskRunId, snapshot, at, metadata, aspect) => addOne(createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata, void 0, aspect)),
|
|
203
203
|
addTaskWithSources,
|
|
204
204
|
addText: (text, at) => {
|
|
205
205
|
add([createTextBoardItem(text, at.x, at.y, styles().text.color)]);
|
|
@@ -11,7 +11,7 @@ import "../model/index.js";
|
|
|
11
11
|
import { AlignMode, DistributeAxis } from "./align.js";
|
|
12
12
|
import { BoardFocusOptions } from "./camera.js";
|
|
13
13
|
import { BoardSelectionTransform, BoardTransformControl } from "./selection-transform.js";
|
|
14
|
-
import { BoardInteraction, BoardPointerEvent } from "./interaction.js";
|
|
14
|
+
import { BoardInteraction, BoardPointerEvent, BoardTransformEnd } from "./interaction.js";
|
|
15
15
|
import { BoardAppMetadata, BoardTaskSource } from "./creation.js";
|
|
16
16
|
import { BoardClipboardPayload } from "./clipboard.js";
|
|
17
17
|
import { BoardNaturalSize } from "./snapshots.js";
|
|
@@ -126,6 +126,7 @@ declare function createBoardEditor(options: BoardEditorOptions): {
|
|
|
126
126
|
width: number;
|
|
127
127
|
height: number;
|
|
128
128
|
};
|
|
129
|
+
controlOutset: number;
|
|
129
130
|
loadDocument: (next: BoardDocument, key?: string) => void;
|
|
130
131
|
undo: () => void;
|
|
131
132
|
redo: () => void;
|
|
@@ -178,8 +179,8 @@ declare function createBoardEditor(options: BoardEditorOptions): {
|
|
|
178
179
|
kind?: BoardMediaItemKind;
|
|
179
180
|
snapshot?: BoardMediaSnapshot;
|
|
180
181
|
}) => string;
|
|
181
|
-
addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown
|
|
182
|
-
addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown
|
|
182
|
+
addTask: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, metadata?: Record<string, unknown>, aspect?: number) => string;
|
|
183
|
+
addTaskWithSources: (taskRunId: string, snapshot: BoardTaskSnapshot, at: WorldPoint, sources: BoardTaskSource[], metadata?: Record<string, unknown>, aspect?: number) => string | null;
|
|
183
184
|
addText: (text: string, at: WorldPoint) => void;
|
|
184
185
|
addShape: (at: WorldPoint, geometry?: ShapeKind) => void;
|
|
185
186
|
addFrame: (at: WorldPoint) => void;
|
|
@@ -204,7 +205,6 @@ declare function createBoardEditor(options: BoardEditorOptions): {
|
|
|
204
205
|
snapshot: BoardFileSnapshotFacts;
|
|
205
206
|
replace?: boolean;
|
|
206
207
|
}>) => void;
|
|
207
|
-
applyTaskSnapshots: (snapshots: ReadonlyMap<string, BoardTaskSnapshot>) => void;
|
|
208
208
|
setPlayhead: (next: BoardPlayhead | null) => void;
|
|
209
209
|
setRecording: (value: boolean) => void;
|
|
210
210
|
keyframeState: (property: string) => "animated" | "keyframe" | "none";
|
|
@@ -217,6 +217,7 @@ declare function createBoardEditor(options: BoardEditorOptions): {
|
|
|
217
217
|
pointerMove: (event: BoardPointerEvent) => void;
|
|
218
218
|
pointerUp: (event: BoardPointerEvent) => void;
|
|
219
219
|
pointerLeave: () => void;
|
|
220
|
+
subscribeTransforms: (listener: (event: BoardTransformEnd) => void) => () => void;
|
|
220
221
|
cancelPointerInteraction: () => void;
|
|
221
222
|
destroy(): void;
|
|
222
223
|
};
|
|
@@ -53,6 +53,7 @@ function createBoardEditor(options) {
|
|
|
53
53
|
width: 0,
|
|
54
54
|
height: 0
|
|
55
55
|
},
|
|
56
|
+
controlOutset: 0,
|
|
56
57
|
cameraPolicy: preferences?.cameraPolicy ?? "follow",
|
|
57
58
|
tool: options.initialTool ?? "select",
|
|
58
59
|
toolStyles: preferences?.toolStyles ?? createBoardToolStyles(),
|
|
@@ -339,6 +340,12 @@ function createBoardEditor(options) {
|
|
|
339
340
|
set surfaceSize(value) {
|
|
340
341
|
state.surfaceSize = value;
|
|
341
342
|
},
|
|
343
|
+
get controlOutset() {
|
|
344
|
+
return read("selection", () => state.controlOutset);
|
|
345
|
+
},
|
|
346
|
+
set controlOutset(value) {
|
|
347
|
+
if (value !== state.controlOutset) state.controlOutset = value;
|
|
348
|
+
},
|
|
342
349
|
loadDocument: remote.loadDocument,
|
|
343
350
|
undo: document.undo,
|
|
344
351
|
redo: document.redo,
|
|
@@ -409,7 +416,6 @@ function createBoardEditor(options) {
|
|
|
409
416
|
applyMediaFileChange: snapshots.applyMediaFileChange,
|
|
410
417
|
adoptMediaNaturalSizes: snapshots.adoptMediaNaturalSizes,
|
|
411
418
|
applyFileSnapshots: snapshots.applyFileSnapshots,
|
|
412
|
-
applyTaskSnapshots: snapshots.applyTaskSnapshots,
|
|
413
419
|
setPlayhead: animation.setPlayhead,
|
|
414
420
|
setRecording: animation.setRecording,
|
|
415
421
|
keyframeState: tracked([
|
|
@@ -427,6 +433,7 @@ function createBoardEditor(options) {
|
|
|
427
433
|
pointerMove: interaction.pointerMove,
|
|
428
434
|
pointerUp: interaction.pointerUp,
|
|
429
435
|
pointerLeave: interaction.pointerLeave,
|
|
436
|
+
subscribeTransforms: interaction.subscribeTransforms,
|
|
430
437
|
cancelPointerInteraction: interaction.cancel,
|
|
431
438
|
destroy() {
|
|
432
439
|
stopViewState();
|
|
@@ -2,7 +2,7 @@ import { BOARD_CLIPBOARD_MIME } from "../../protocol/dist/board.js";
|
|
|
2
2
|
import { AlignMode, DistributeAxis, alignFrames, distributeFrames, framesBounds } from "./align.js";
|
|
3
3
|
import { BoardFocusOptions } from "./camera.js";
|
|
4
4
|
import { BoardSelectionTransform, BoardTransformControl, resizeCursorForHandle, resolveSelectionTransform, selectionTransformControlAt } from "./selection-transform.js";
|
|
5
|
-
import { BoardInteraction, BoardPointerEvent } from "./interaction.js";
|
|
5
|
+
import { BoardInteraction, BoardPointerEvent, BoardTransformEnd } from "./interaction.js";
|
|
6
6
|
import { BoardAppMetadata, BoardTaskSource } from "./creation.js";
|
|
7
7
|
import { BOARD_CLIPBOARD_VERSION, BoardClipboardPayload, DUPLICATE_OFFSET, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, defaultPasteOffset, encodeClipboard, materializeClipboard, parseClipboard, remapItems } from "./clipboard.js";
|
|
8
8
|
import { BoardNaturalSize } from "./snapshots.js";
|
|
@@ -15,4 +15,4 @@ import { BoardEditPlayhead, diffBoardEdits, isEmptyBoardPatch, readPath, routeBo
|
|
|
15
15
|
import { BoardEditor, createBoardEditor } from "./editor.js";
|
|
16
16
|
import { SpatialEntry, SpatialIndex, createSpatialIndex } from "./spatial.js";
|
|
17
17
|
import { normalizeWheelDelta, wheelZoomFactor } from "./wheel.js";
|
|
18
|
-
export { AlignMode, BOARD_CLIPBOARD_MIME, BOARD_CLIPBOARD_VERSION, BOARD_HAND_TAP_SLOP, type BoardAppMetadata, type BoardCameraPolicy, BoardClipboardPayload, BoardEditPlayhead, type BoardEditor, type BoardEditorChannel, type BoardEditorListener, type BoardEditorOptions, type BoardEditorPreferences, type BoardFocusOptions, type BoardInteraction, type BoardNaturalSize, type BoardPlayhead, type BoardPointerEvent, BoardSelectionTransform, type BoardTaskSource, BoardToolId, BoardTransformControl, type BoardViewState, CONNECTION_PORT_HIT_RADIUS, CONNECTION_PORT_OFFSET, CONNECTION_PORT_RADIUS, CONNECTION_PORT_TOUCH_HIT_RADIUS, CONNECTION_SIDES, ConnectionPort, DUPLICATE_OFFSET, DistributeAxis, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, SnapAxis, SnapGuide, SnapOptions, SnapResult, SpatialEntry, SpatialIndex, alignFrames, canTapSelectWithHand, computeSnap, connectionPortAt, connectionPorts, createBoardEditor, createSpatialIndex, defaultBoardTool, defaultPasteOffset, diffBoardEdits, distributeFrames, encodeClipboard, framesBounds, isContinuousBoardTool, isCreationBoardTool, isEmptyBoardPatch, isWithinHandTapSlop, materializeClipboard, normalizeWheelDelta, parseClipboard, portHitRadius, readPath, remapItems, resizeCursorForHandle, resolveSelectionTransform, routeBoardEdits, sameJson, selectionTransformControlAt, wheelZoomFactor, writePath };
|
|
18
|
+
export { AlignMode, BOARD_CLIPBOARD_MIME, BOARD_CLIPBOARD_VERSION, BOARD_HAND_TAP_SLOP, type BoardAppMetadata, type BoardCameraPolicy, BoardClipboardPayload, BoardEditPlayhead, type BoardEditor, type BoardEditorChannel, type BoardEditorListener, type BoardEditorOptions, type BoardEditorPreferences, type BoardFocusOptions, type BoardInteraction, type BoardNaturalSize, type BoardPlayhead, type BoardPointerEvent, BoardSelectionTransform, type BoardTaskSource, BoardToolId, BoardTransformControl, type BoardTransformEnd, type BoardViewState, CONNECTION_PORT_HIT_RADIUS, CONNECTION_PORT_OFFSET, CONNECTION_PORT_RADIUS, CONNECTION_PORT_TOUCH_HIT_RADIUS, CONNECTION_SIDES, ConnectionPort, DUPLICATE_OFFSET, DistributeAxis, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, SnapAxis, SnapGuide, SnapOptions, SnapResult, SpatialEntry, SpatialIndex, alignFrames, canTapSelectWithHand, computeSnap, connectionPortAt, connectionPorts, createBoardEditor, createSpatialIndex, defaultBoardTool, defaultPasteOffset, diffBoardEdits, distributeFrames, encodeClipboard, framesBounds, isContinuousBoardTool, isCreationBoardTool, isEmptyBoardPatch, isWithinHandTapSlop, materializeClipboard, normalizeWheelDelta, parseClipboard, portHitRadius, readPath, remapItems, resizeCursorForHandle, resolveSelectionTransform, routeBoardEdits, sameJson, selectionTransformControlAt, wheelZoomFactor, writePath };
|
|
@@ -28,6 +28,10 @@ type BoardInteraction = {
|
|
|
28
28
|
single: BoardFrame | null;
|
|
29
29
|
bounds: Rect;
|
|
30
30
|
origin: Map<string, BoardFrame>;
|
|
31
|
+
grab: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
};
|
|
31
35
|
moved: boolean;
|
|
32
36
|
} | {
|
|
33
37
|
type: "rotating";
|
|
@@ -90,6 +94,11 @@ type BoardPointerEvent = {
|
|
|
90
94
|
pressure: number;
|
|
91
95
|
samples?: readonly BoardDrawInputSample[];
|
|
92
96
|
};
|
|
97
|
+
type BoardTransformEnd = {
|
|
98
|
+
ids: string[];
|
|
99
|
+
operation: "move" | "resize" | "rotate" | "arrow";
|
|
100
|
+
cancelled: boolean;
|
|
101
|
+
};
|
|
93
102
|
declare function createInteractionModule(ctx: EditorContext): {
|
|
94
103
|
marquee: () => Rect | null;
|
|
95
104
|
hoveredControl: () => BoardTransformControl | null;
|
|
@@ -108,8 +117,9 @@ declare function createInteractionModule(ctx: EditorContext): {
|
|
|
108
117
|
pointerUp: (event: BoardPointerEvent) => void;
|
|
109
118
|
pointerLeave: () => void;
|
|
110
119
|
cancel: () => void;
|
|
111
|
-
|
|
120
|
+
subscribeTransforms(listener: (event: BoardTransformEnd) => void): () => void;
|
|
121
|
+
destroy(): void;
|
|
112
122
|
};
|
|
113
123
|
type InteractionModule = ReturnType<typeof createInteractionModule>;
|
|
114
124
|
//#endregion
|
|
115
|
-
export { BoardInteraction, BoardPointerEvent, InteractionModule, createInteractionModule };
|
|
125
|
+
export { BoardInteraction, BoardPointerEvent, BoardTransformEnd, InteractionModule, createInteractionModule };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clampZoom, normalizeRotation, panBy, rectCenter, rectsIntersect, resizeFrame, rotateFrames, scaleFrames, screenPoint, selectionBounds, worldPoint, zoomAround } from "../model/geometry.js";
|
|
1
|
+
import { clampZoom, frameHandlePosition, normalizeRotation, panBy, rectCenter, rectsIntersect, resizeFrame, rotateFrames, scaleFrames, screenPoint, selectionBounds, worldPoint, zoomAround } from "../model/geometry.js";
|
|
2
2
|
import { resolveSceneArrow } from "../model/arrow-geometry.js";
|
|
3
3
|
import { appendBoardDrawSamples } from "../model/draw-input.js";
|
|
4
4
|
import { createBoardItemId } from "../model/items/id.js";
|
|
@@ -15,6 +15,23 @@ function angleFrom(center, point) {
|
|
|
15
15
|
return Math.atan2(point.y - center.y, point.x - center.x) * 180 / Math.PI;
|
|
16
16
|
}
|
|
17
17
|
function createInteractionModule(ctx) {
|
|
18
|
+
const transformListeners = /* @__PURE__ */ new Set();
|
|
19
|
+
function transformEnd(gesture, cancelled) {
|
|
20
|
+
if (!transformListeners.size || !("moved" in gesture) || !gesture.moved) return;
|
|
21
|
+
let event;
|
|
22
|
+
if (gesture.type === "translating" || gesture.type === "resizing" || gesture.type === "rotating") event = {
|
|
23
|
+
ids: [...gesture.origin.keys()],
|
|
24
|
+
operation: gesture.type === "translating" ? "move" : gesture.type === "resizing" ? "resize" : "rotate",
|
|
25
|
+
cancelled
|
|
26
|
+
};
|
|
27
|
+
else if (gesture.type === "draggingArrowHandle") event = {
|
|
28
|
+
ids: [gesture.arrowId],
|
|
29
|
+
operation: "arrow",
|
|
30
|
+
cancelled
|
|
31
|
+
};
|
|
32
|
+
else return;
|
|
33
|
+
for (const listener of transformListeners) listener(event);
|
|
34
|
+
}
|
|
18
35
|
const { state, options, registry } = ctx;
|
|
19
36
|
const activePointers = /* @__PURE__ */ new Map();
|
|
20
37
|
let pinch = null;
|
|
@@ -34,8 +51,9 @@ function createInteractionModule(ctx) {
|
|
|
34
51
|
state.hoverPoint,
|
|
35
52
|
state.hoverPointerType,
|
|
36
53
|
state.camera.zoom,
|
|
37
|
-
ctx.selection.transform()
|
|
38
|
-
|
|
54
|
+
ctx.selection.transform(),
|
|
55
|
+
state.controlOutset
|
|
56
|
+
], (interaction, tool, hoverPoint, pointerType, zoom, transform, outset) => interaction.type === "idle" && !pinch && tool === "select" && hoverPoint ? selectionTransformControlAt(transform, hoverPoint, zoom, pointerType, outset) : null);
|
|
39
57
|
function gridSnapSize() {
|
|
40
58
|
const grid = state.base.board.grid;
|
|
41
59
|
return grid?.visible === true ? grid.size ?? 0 : 0;
|
|
@@ -227,7 +245,8 @@ function createInteractionModule(ctx) {
|
|
|
227
245
|
return;
|
|
228
246
|
}
|
|
229
247
|
const bounds = ctx.selection.bounds();
|
|
230
|
-
const
|
|
248
|
+
const transform = ctx.selection.transform();
|
|
249
|
+
const control = selectionTransformControlAt(transform, event.world, camera.zoom, event.pointerType, state.controlOutset);
|
|
231
250
|
if (control?.kind === "rotate" && bounds) {
|
|
232
251
|
const pivot = rectCenter(bounds);
|
|
233
252
|
state.interaction = {
|
|
@@ -240,15 +259,21 @@ function createInteractionModule(ctx) {
|
|
|
240
259
|
};
|
|
241
260
|
return;
|
|
242
261
|
}
|
|
243
|
-
if (control?.kind === "resize" && bounds) {
|
|
262
|
+
if (control?.kind === "resize" && bounds && transform) {
|
|
244
263
|
const origin = ctx.selection.framesFor(ctx.selection.movable());
|
|
245
264
|
const single = origin.size === 1 ? { ...[...origin.values()][0] } : null;
|
|
265
|
+
const edge = frameHandlePosition(transform.frame, control.handle);
|
|
266
|
+
const grab = {
|
|
267
|
+
x: edge.x - event.world.x,
|
|
268
|
+
y: edge.y - event.world.y
|
|
269
|
+
};
|
|
246
270
|
state.interaction = {
|
|
247
271
|
type: "resizing",
|
|
248
272
|
handle: control.handle,
|
|
249
273
|
single,
|
|
250
274
|
bounds,
|
|
251
275
|
origin,
|
|
276
|
+
grab,
|
|
252
277
|
moved: false
|
|
253
278
|
};
|
|
254
279
|
return;
|
|
@@ -332,13 +357,14 @@ function createInteractionModule(ctx) {
|
|
|
332
357
|
moved: true
|
|
333
358
|
};
|
|
334
359
|
const frames = /* @__PURE__ */ new Map();
|
|
360
|
+
const point = worldPoint(event.world.x + gesture.grab.x, event.world.y + gesture.grab.y);
|
|
335
361
|
if (gesture.single) {
|
|
336
362
|
const id = [...gesture.origin.keys()][0];
|
|
337
363
|
const item = state.scene.get(id);
|
|
338
364
|
const keepAspect = item ? registry.capabilities(item).aspectLocked || event.shiftKey : event.shiftKey;
|
|
339
|
-
frames.set(id, resizeFrame(gesture.single, gesture.handle,
|
|
365
|
+
frames.set(id, resizeFrame(gesture.single, gesture.handle, point, void 0, keepAspect));
|
|
340
366
|
} else {
|
|
341
|
-
const scaled = scaleFrames([...gesture.origin.values()], gesture.bounds, gesture.handle,
|
|
367
|
+
const scaled = scaleFrames([...gesture.origin.values()], gesture.bounds, gesture.handle, point);
|
|
342
368
|
[...gesture.origin.keys()].forEach((id, index) => {
|
|
343
369
|
const frame = scaled[index];
|
|
344
370
|
if (frame) frames.set(id, frame);
|
|
@@ -493,6 +519,10 @@ function createInteractionModule(ctx) {
|
|
|
493
519
|
if (activePointers.size > 0) return;
|
|
494
520
|
const gesture = state.interaction;
|
|
495
521
|
if (gesture.type === "drawing" && gesture.pointerId !== event.pointerId) return;
|
|
522
|
+
if (event.cancelled) {
|
|
523
|
+
cancel();
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
496
526
|
state.snapGuides = [];
|
|
497
527
|
state.interaction = { type: "idle" };
|
|
498
528
|
switch (gesture.type) {
|
|
@@ -531,6 +561,7 @@ function createInteractionModule(ctx) {
|
|
|
531
561
|
case "creatingBox": ctx.creation.commitBox(gesture);
|
|
532
562
|
}
|
|
533
563
|
ctx.remote.flushPending();
|
|
564
|
+
transformEnd(gesture, false);
|
|
534
565
|
}
|
|
535
566
|
function pointerLeave() {
|
|
536
567
|
if (state.interaction.type !== "idle") return;
|
|
@@ -538,12 +569,14 @@ function createInteractionModule(ctx) {
|
|
|
538
569
|
state.hoverId = null;
|
|
539
570
|
}
|
|
540
571
|
function cancel() {
|
|
572
|
+
const gesture = state.interaction;
|
|
541
573
|
activePointers.clear();
|
|
542
574
|
pinch = null;
|
|
543
575
|
state.snapGuides = [];
|
|
544
576
|
state.interaction = { type: "idle" };
|
|
545
577
|
ctx.document.discardDraft();
|
|
546
578
|
ctx.remote.flushPending();
|
|
579
|
+
transformEnd(gesture, true);
|
|
547
580
|
}
|
|
548
581
|
const ports = memo(() => [
|
|
549
582
|
state.scene,
|
|
@@ -577,7 +610,16 @@ function createInteractionModule(ctx) {
|
|
|
577
610
|
pointerUp,
|
|
578
611
|
pointerLeave,
|
|
579
612
|
cancel,
|
|
580
|
-
|
|
613
|
+
subscribeTransforms(listener) {
|
|
614
|
+
transformListeners.add(listener);
|
|
615
|
+
return () => {
|
|
616
|
+
transformListeners.delete(listener);
|
|
617
|
+
};
|
|
618
|
+
},
|
|
619
|
+
destroy() {
|
|
620
|
+
activePointers.clear();
|
|
621
|
+
transformListeners.clear();
|
|
622
|
+
}
|
|
581
623
|
};
|
|
582
624
|
}
|
|
583
625
|
//#endregion
|
|
@@ -19,6 +19,6 @@ type BoardSelectionTransform = {
|
|
|
19
19
|
canRotate: boolean;
|
|
20
20
|
};
|
|
21
21
|
declare function resolveSelectionTransform(registry: BoardRegistry, items: BoardSceneItem[], bounds: Rect | null): BoardSelectionTransform | null;
|
|
22
|
-
declare function selectionTransformControlAt(
|
|
22
|
+
declare function selectionTransformControlAt(selection: BoardSelectionTransform | null, point: WorldPoint, zoom: number, pointerType: string, outset?: number): BoardTransformControl | null;
|
|
23
23
|
//#endregion
|
|
24
24
|
export { BoardSelectionTransform, BoardTransformControl, resizeCursorForHandle, resolveSelectionTransform, selectionTransformControlAt };
|
|
@@ -51,10 +51,21 @@ function controlHitRadius(pointerType) {
|
|
|
51
51
|
if (pointerType === "pen") return 12;
|
|
52
52
|
return 8;
|
|
53
53
|
}
|
|
54
|
-
function selectionTransformControlAt(
|
|
55
|
-
if (!
|
|
54
|
+
function selectionTransformControlAt(selection, point, zoom, pointerType, outset = 0) {
|
|
55
|
+
if (!selection) return null;
|
|
56
56
|
const screenRadius = controlHitRadius(pointerType);
|
|
57
57
|
const radius = screenRadius / Math.max(zoom, 1e-4);
|
|
58
|
+
const grow = outset / Math.max(zoom, 1e-4);
|
|
59
|
+
const transform = grow > 0 ? {
|
|
60
|
+
...selection,
|
|
61
|
+
frame: {
|
|
62
|
+
...selection.frame,
|
|
63
|
+
x: selection.frame.x - grow,
|
|
64
|
+
y: selection.frame.y - grow,
|
|
65
|
+
width: selection.frame.width + grow * 2,
|
|
66
|
+
height: selection.frame.height + grow * 2
|
|
67
|
+
}
|
|
68
|
+
} : selection;
|
|
58
69
|
if (transform.resizeMode !== "none") for (const handle of CORNER_RESIZE_HANDLES) {
|
|
59
70
|
const position = frameHandlePosition(transform.frame, handle);
|
|
60
71
|
if (Math.hypot(position.x - point.x, position.y - point.y) <= radius) return {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { BoardTaskSnapshot } from "../../protocol/dist/board-model.js";
|
|
2
1
|
import { BoardFileSnapshotFacts } from "../model/file-snapshot.js";
|
|
3
2
|
import "../model/index.js";
|
|
4
3
|
import { EditorContext } from "./context.js";
|
|
@@ -20,7 +19,6 @@ declare function createSnapshotsModule(ctx: EditorContext): {
|
|
|
20
19
|
snapshot: BoardFileSnapshotFacts;
|
|
21
20
|
replace?: boolean;
|
|
22
21
|
}>) => void;
|
|
23
|
-
applyTaskSnapshots: (snapshots: ReadonlyMap<string, BoardTaskSnapshot>) => void;
|
|
24
22
|
};
|
|
25
23
|
type SnapshotsModule = ReturnType<typeof createSnapshotsModule>;
|
|
26
24
|
//#endregion
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
import { featuredTaskArtifact } from "../../protocol/dist/board-task.js";
|
|
1
2
|
import { mergeFileSnapshot } from "../model/file-preview.js";
|
|
2
|
-
import { featuredTaskArtifact } from "../model/task.js";
|
|
3
3
|
import { sameJson } from "./document-edits.js";
|
|
4
4
|
//#region src/board/editor/snapshots.ts
|
|
5
5
|
function fitHeight(item, natural) {
|
|
6
6
|
const height = item.size.width * natural.height / natural.width;
|
|
7
7
|
if (!Number.isFinite(height) || height <= 0) return null;
|
|
8
|
+
const shift = item.type === "task" ? 0 : (item.size.height - height) / 2;
|
|
8
9
|
return {
|
|
9
10
|
...item,
|
|
10
11
|
position: {
|
|
11
12
|
x: item.position.x,
|
|
12
|
-
y: item.position.y +
|
|
13
|
+
y: item.position.y + shift
|
|
13
14
|
},
|
|
14
15
|
size: {
|
|
15
16
|
...item.size,
|
|
@@ -105,27 +106,10 @@ function createSnapshotsModule(ctx) {
|
|
|
105
106
|
}
|
|
106
107
|
commit(changes);
|
|
107
108
|
}
|
|
108
|
-
function applyTaskSnapshots(snapshots) {
|
|
109
|
-
if (snapshots.size === 0) return;
|
|
110
|
-
const changes = /* @__PURE__ */ new Map();
|
|
111
|
-
for (const [id, item] of Object.entries(state.base.items)) {
|
|
112
|
-
if (item.type !== "task") continue;
|
|
113
|
-
const snapshot = snapshots.get(item.props.taskRunId);
|
|
114
|
-
if (snapshot && !sameJson(snapshot, item.props.snapshot)) changes.set(id, {
|
|
115
|
-
...item,
|
|
116
|
-
props: {
|
|
117
|
-
...item.props,
|
|
118
|
-
snapshot
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
commit(changes);
|
|
123
|
-
}
|
|
124
109
|
return {
|
|
125
110
|
applyMediaFileChange,
|
|
126
111
|
adoptMediaNaturalSizes,
|
|
127
|
-
applyFileSnapshots
|
|
128
|
-
applyTaskSnapshots
|
|
112
|
+
applyFileSnapshots
|
|
129
113
|
};
|
|
130
114
|
}
|
|
131
115
|
//#endregion
|
|
@@ -6,6 +6,7 @@ import { BoardEvaluation } from "../model/animation.js";
|
|
|
6
6
|
import { BoardAnimationSampler } from "../model/sampling.js";
|
|
7
7
|
import { BoardExportPlan, BoardExportPlanInput, BoardExportRegion } from "../model/export-plan.js";
|
|
8
8
|
import { BoardShapeColors } from "../model/palette.js";
|
|
9
|
+
import { BoardRenderStyle } from "../render/style.js";
|
|
9
10
|
import { BoardCardRendererResolver, BoardRenderPalette, BoardScriptHost } from "../render/renderers/board-renderer-registry.js";
|
|
10
11
|
import "../render/index.js";
|
|
11
12
|
import { BoardExportScene, BoardExportSceneInput, createBoardExportScene } from "./scene.js";
|
|
@@ -29,6 +30,7 @@ type BoardExportOptions = {
|
|
|
29
30
|
background?: BoardExportBackground;
|
|
30
31
|
palette?: Partial<BoardRenderPalette>;
|
|
31
32
|
colors?: BoardShapeColors;
|
|
33
|
+
style?: Partial<BoardRenderStyle>;
|
|
32
34
|
textures?: Map<string, Texture>;
|
|
33
35
|
scripts?: BoardScriptHost;
|
|
34
36
|
assetKey?: (item: BoardSceneItem) => string | null;
|
|
@@ -3,6 +3,7 @@ import "../../protocol/dist/index.js";
|
|
|
3
3
|
import { Rect } from "../model/geometry.js";
|
|
4
4
|
import { BoardScene, BoardSceneItem } from "../model/scene.js";
|
|
5
5
|
import { BoardShapeColors } from "../model/palette.js";
|
|
6
|
+
import { BoardRenderStyle } from "../render/style.js";
|
|
6
7
|
import { BoardCardRendererResolver, BoardRenderContext, BoardRenderPalette } from "../render/renderers/board-renderer-registry.js";
|
|
7
8
|
import "../render/index.js";
|
|
8
9
|
import { Container, Texture } from "pixi.js";
|
|
@@ -18,6 +19,7 @@ type BoardExportSceneInput = {
|
|
|
18
19
|
colorScheme: "dark" | "light";
|
|
19
20
|
palette?: Partial<BoardRenderPalette>;
|
|
20
21
|
colors?: BoardShapeColors;
|
|
22
|
+
style?: Partial<BoardRenderStyle>;
|
|
21
23
|
textures?: Map<string, Texture>;
|
|
22
24
|
scripts?: BoardRenderContext["scripts"];
|
|
23
25
|
assetKey?: (item: BoardSceneItem) => string | null;
|
|
@@ -2,7 +2,8 @@ import { buildFallbackShapeColors } from "../model/palette.js";
|
|
|
2
2
|
import { imageAssetKey } from "../model/image-key.js";
|
|
3
3
|
import { defaultBoardPalette } from "../render/palette.js";
|
|
4
4
|
import { clippingAncestor, isClippingFrame, syncClipGroup } from "../render/clip.js";
|
|
5
|
-
import { getBoardCardRenderer } from "../render/renderers/board-renderer-registry.js";
|
|
5
|
+
import { DEFAULT_BOARD_RENDER_LABELS, getBoardCardRenderer } from "../render/renderers/board-renderer-registry.js";
|
|
6
|
+
import { resolveBoardRenderStyle } from "../render/style.js";
|
|
6
7
|
import { Container, Graphics, Sprite, TilingSprite } from "pixi.js";
|
|
7
8
|
//#region src/board/export/scene.ts
|
|
8
9
|
function buildContext(input) {
|
|
@@ -17,14 +18,20 @@ function buildContext(input) {
|
|
|
17
18
|
selectedIds: /* @__PURE__ */ new Set(),
|
|
18
19
|
hoveredId: null,
|
|
19
20
|
resizingIds: /* @__PURE__ */ new Set(),
|
|
21
|
+
liftedIds: /* @__PURE__ */ new Set(),
|
|
20
22
|
palette: {
|
|
21
23
|
...defaultBoardPalette(input.colorScheme),
|
|
22
24
|
...input.palette
|
|
23
25
|
},
|
|
24
26
|
colors: input.colors ?? buildFallbackShapeColors(input.colorScheme),
|
|
25
27
|
colorScheme: input.colorScheme,
|
|
28
|
+
style: resolveBoardRenderStyle(input.colorScheme, input.style),
|
|
29
|
+
themeKey: "export",
|
|
26
30
|
rendererType: "canvas",
|
|
27
31
|
zoom: input.scale,
|
|
32
|
+
reducedMotion: true,
|
|
33
|
+
labels: DEFAULT_BOARD_RENDER_LABELS,
|
|
34
|
+
taskRun: () => null,
|
|
28
35
|
scripts: input.scripts,
|
|
29
36
|
assetKey: input.assetKey ?? imageAssetKey,
|
|
30
37
|
getTexture: (key) => textures.get(key) ?? null,
|
|
@@ -7,7 +7,6 @@ type FilePreviewKind = "cover" | "text" | "blank";
|
|
|
7
7
|
* sync with them.
|
|
8
8
|
*/
|
|
9
9
|
declare function filePreviewKind(snapshot: BoardFileSnapshotFacts | undefined): FilePreviewKind;
|
|
10
|
-
/** Short uppercase type label for a file card (`MD`, `JSON`, `FILE`). */
|
|
11
10
|
declare function fileTypeLabel(path: string): string;
|
|
12
11
|
/** Human-readable byte size. Kept as a public helper for custom card renderers. */
|
|
13
12
|
declare function formatFileSize(bytes: number | undefined): string;
|
|
@@ -11,13 +11,22 @@ function filePreviewKind(snapshot) {
|
|
|
11
11
|
if (snapshot.excerpt) return "text";
|
|
12
12
|
return "blank";
|
|
13
13
|
}
|
|
14
|
-
/** Short uppercase type label for a file card (`MD`, `JSON`, `FILE`). */
|
|
15
14
|
function fileTypeLabel(path) {
|
|
15
|
+
if (isWebPageUrl(path)) return "LINK";
|
|
16
16
|
const name = fileBaseName(path);
|
|
17
17
|
const dot = name.lastIndexOf(".");
|
|
18
18
|
if (dot <= 0) return (name.replace(/^\./, "") || "file").toUpperCase();
|
|
19
19
|
return name.slice(dot + 1).toUpperCase();
|
|
20
20
|
}
|
|
21
|
+
function isWebPageUrl(path) {
|
|
22
|
+
if (!/^https?:\/\//i.test(path)) return false;
|
|
23
|
+
try {
|
|
24
|
+
const last = new URL(path).pathname.split("/").filter(Boolean).pop() ?? "";
|
|
25
|
+
return !/\.[a-z\d]{1,8}$/i.test(last);
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
21
30
|
/** Human-readable byte size. Kept as a public helper for custom card renderers. */
|
|
22
31
|
function formatFileSize(bytes) {
|
|
23
32
|
if (bytes === void 0 || !Number.isFinite(bytes) || bytes < 0) return "";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseBoardMediaSource } from "../../protocol/dist/board-model.js";
|
|
2
|
-
import { featuredTaskArtifact, taskArtifactPreviewUrl } from "
|
|
2
|
+
import { featuredTaskArtifact, taskArtifactPreviewUrl } from "../../protocol/dist/board-task.js";
|
|
3
3
|
//#region src/board/model/image-key.ts
|
|
4
4
|
function imageAssetKey(item) {
|
|
5
5
|
if (item.type === "image") {
|