chalkbridge 1.0.0 → 1.0.1
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/CHANGELOG.md +53 -0
- package/dist/{chunk-HFKCLTVU.js → chunk-SHJT5BF3.js} +10 -4
- package/dist/cli.js +125 -27
- package/dist/index.js +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# chalkbridge
|
|
2
|
+
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
**Arrows are drawn where they belong, the first time.**
|
|
6
|
+
|
|
7
|
+
Six bugs of the same family, all found by drawing one real diagram — 29 boxes, 34 arrows —
|
|
8
|
+
which cost roughly forty tool calls of workaround. Every one of them answered `applied`
|
|
9
|
+
and then did less than that, which is the most expensive shape a bug can have: the caller
|
|
10
|
+
spends its next calls re-reading to find out why.
|
|
11
|
+
|
|
12
|
+
- **`chalk_connect` now places the arrow.** The expander returns a stub at the origin with
|
|
13
|
+
`points: [[0.5, 0], [99.5, 0]]` — the bindings were correct and the geometry was a
|
|
14
|
+
placeholder — so every new arrow landed in the same corner and looked like a scribble.
|
|
15
|
+
- **`chalk_update_elements` redraws the arrows of a box it moved**, and brings the box's
|
|
16
|
+
caption with it. Only `chalk_arrange` used to do either, so the only way to repair a
|
|
17
|
+
diagram was to displace every element and align it back, one row at a time.
|
|
18
|
+
- **`chalk_frame` works more than once.** Upstream rebuilds a frame from its `children`,
|
|
19
|
+
which a frame that already exists does not have, so it threw — and *every second frame
|
|
20
|
+
failed*, whatever was in it.
|
|
21
|
+
- **`patch.role` restyles** instead of writing a junk `role` field onto the element.
|
|
22
|
+
Explicit properties still win over what the role implies.
|
|
23
|
+
- **`patch.label` renames the caption** instead of writing a junk `label` field, and says
|
|
24
|
+
so plainly when there is no caption to rename rather than silently doing nothing.
|
|
25
|
+
- **Deleting a container tombstones its caption**, instead of leaving a word floating on
|
|
26
|
+
the canvas that nothing owns.
|
|
27
|
+
|
|
28
|
+
`applied` and `deleted` now list only the ids you asked about. A caption that followed its
|
|
29
|
+
box is a consequence, not a result you should have to reason about.
|
|
30
|
+
|
|
31
|
+
No schema changes — every tool takes and returns exactly what it did in 1.0.0.
|
|
32
|
+
|
|
33
|
+
## 1.0.0
|
|
34
|
+
|
|
35
|
+
First release.
|
|
36
|
+
|
|
37
|
+
A self-hosted Excalidraw canvas that Claude Code can read and draw on. The canvas opens in
|
|
38
|
+
your browser, you select on it and ask in the terminal, and the diagram is a plain
|
|
39
|
+
`.excalidraw` file in your repository.
|
|
40
|
+
|
|
41
|
+
- **Read** — `chalk_describe_scene` renders the canvas as a compact text form, 3.7× smaller
|
|
42
|
+
than the same structure as JSON. `chalk_get_selection` answers "what are these?"
|
|
43
|
+
including the edges that cross the selection boundary.
|
|
44
|
+
- **Draw** — boxes by `role` rather than by colour, arrows bound to their boxes, layered
|
|
45
|
+
graph layout, frames, groups, annotations. Writes succeed with no browser connected.
|
|
46
|
+
- **Illustrate** — a machine-wide icon catalog with FTS5 search; a hand-drawn set is
|
|
47
|
+
bundled and `chalk_install_pack("devicon")` adds technology logos in one call. Vendor
|
|
48
|
+
sets return instructions and are never downloaded for you.
|
|
49
|
+
- **Export** — PNG and SVG rendered by your own browser, and `.excalidraw` written where
|
|
50
|
+
you asked.
|
|
51
|
+
- **Undo** — labelled snapshots.
|
|
52
|
+
|
|
53
|
+
Node 22 or newer. No native modules, no container, nothing leaves the machine.
|
|
@@ -1266,6 +1266,11 @@ var unknownElement = (id) => err("UNKNOWN_ELEMENT", `no element ${id} in this sc
|
|
|
1266
1266
|
tool: "chalk_describe_scene",
|
|
1267
1267
|
detail: { id }
|
|
1268
1268
|
});
|
|
1269
|
+
var noBoundLabel = (id) => err("NO_BOUND_LABEL", `element ${id} has no caption to rename`, {
|
|
1270
|
+
hint: "draw it with a label in the first place, or put the text beside it with chalk_annotate",
|
|
1271
|
+
tool: "chalk_annotate",
|
|
1272
|
+
detail: { id }
|
|
1273
|
+
});
|
|
1269
1274
|
var emptySelection = (tool) => err("EMPTY_SELECTION", "no elements named \u2014 there is nothing to act on", {
|
|
1270
1275
|
hint: "pass the ids you mean, or call chalk_get_selection to find out what the user is pointing at",
|
|
1271
1276
|
tool: "chalk_get_selection",
|
|
@@ -1453,7 +1458,8 @@ function createNodeSkeletonExpander() {
|
|
|
1453
1458
|
if (!loaded.ok) return loaded;
|
|
1454
1459
|
let raw;
|
|
1455
1460
|
try {
|
|
1456
|
-
|
|
1461
|
+
const convertible = (context ?? []).filter((element) => element.type !== "frame");
|
|
1462
|
+
raw = loaded.value([...convertible, ...skeletons], { regenerateIds: false });
|
|
1457
1463
|
} catch (thrown) {
|
|
1458
1464
|
return fail(expansionFailed(describeThrown(thrown)));
|
|
1459
1465
|
}
|
|
@@ -1489,6 +1495,6 @@ var stable = (element) => {
|
|
|
1489
1495
|
return rest;
|
|
1490
1496
|
};
|
|
1491
1497
|
|
|
1492
|
-
export { AnnotateInput, AnnotateOutput, ApiError, ArrangeOutput, ArrangeToolInput, BINDING_FIELDS, ClientMessage, ConnectOutput, ConnectToolInput, CreateElementsInput, CreateElementsOutput, DebugElementRequest, DeleteElementsInput, DeleteElementsOutput, DescribeSceneInput, DescribeSceneOutput, DetailLevel, EXCALIDRAW_FILE_TYPE, EXCALIDRAW_FILE_VERSION, ElementId, EmbedInput, EmbedOutput, ExcalidrawFile, ExportImageInput, ExportImageOutput, ExportSceneInput, ExportSceneOutput, FLAG_GLYPH, FileId, FocusInput, FocusOutput, FrameId, FrameInput, FrameOutput, GUIDE_TOPICS, GetElementsInput, GetElementsOutput, GetSelectionInput, GetSelectionOutput, GroupId, GroupInput, GroupOutput, GuideTopic, HOUSE, HealthResponse, IconId, IconItem, ImportSceneInput, ImportSceneOutput, Index, InstallPackInput, InstallPackOutput, KIND_ABBREVIATION, ListPacksInput, ListPacksOutput, ListScenesInput, ListScenesOutput, PROTOCOL_VERSION, PackKind, PackName, PackRecipe, PlaceIconInput, PlaceIconOutput, ROUNDNESS, RawElementsInput, RawElementsOutput, ReadMeInput, ReadMeOutput, RemovePackInput, RemovePackOutput, RestoreSnapshotInput, RestoreSnapshotOutput, SERVER_MANAGED_FIELDS, STALE_SELECTION_MS, Scene, SceneElement, SceneId, SceneSettingsInput, SceneSettingsOutput, ScreenshotInput, ScreenshotOutput, ScreenshotResponse, SearchIconsInput, SearchIconsOutput, SnapshotId, SnapshotInput, SnapshotOutput, UngroupInput, UngroupOutput, UpdateElementsInput, UpdateElementsOutput, assertNever, bridgeSpawnFailed, bridgeUnreachable, catalogUnavailable, createNodeSkeletonExpander, deepEqual, describeThrown, diskConflict, emptySelection, expansionFailed, exportFailed, exportTimedOut, fail, frameIdOf, iconIdOf, idCollision, isFail, isOk, noBrowser, noScenes, notAPack, notARepo, ok, packInstallFailed, partition, pathOutsideRepo, point, portUnavailable, refusedFieldMessage, sceneFileMissing, staleVersion, storeUnavailable, unknownElement, unknownIcon, unknownScene, unknownSnapshot, unreadableSceneFile };
|
|
1493
|
-
//# sourceMappingURL=chunk-
|
|
1494
|
-
//# sourceMappingURL=chunk-
|
|
1498
|
+
export { AnnotateInput, AnnotateOutput, ApiError, ArrangeOutput, ArrangeToolInput, BINDING_FIELDS, ClientMessage, ConnectOutput, ConnectToolInput, CreateElementsInput, CreateElementsOutput, DebugElementRequest, DeleteElementsInput, DeleteElementsOutput, DescribeSceneInput, DescribeSceneOutput, DetailLevel, EXCALIDRAW_FILE_TYPE, EXCALIDRAW_FILE_VERSION, ElementId, EmbedInput, EmbedOutput, ExcalidrawFile, ExportImageInput, ExportImageOutput, ExportSceneInput, ExportSceneOutput, FLAG_GLYPH, FileId, FocusInput, FocusOutput, FrameId, FrameInput, FrameOutput, GUIDE_TOPICS, GetElementsInput, GetElementsOutput, GetSelectionInput, GetSelectionOutput, GroupId, GroupInput, GroupOutput, GuideTopic, HOUSE, HealthResponse, IconId, IconItem, ImportSceneInput, ImportSceneOutput, Index, InstallPackInput, InstallPackOutput, KIND_ABBREVIATION, ListPacksInput, ListPacksOutput, ListScenesInput, ListScenesOutput, PROTOCOL_VERSION, PackKind, PackName, PackRecipe, PlaceIconInput, PlaceIconOutput, ROUNDNESS, RawElementsInput, RawElementsOutput, ReadMeInput, ReadMeOutput, RemovePackInput, RemovePackOutput, RestoreSnapshotInput, RestoreSnapshotOutput, SERVER_MANAGED_FIELDS, STALE_SELECTION_MS, Scene, SceneElement, SceneId, SceneSettingsInput, SceneSettingsOutput, ScreenshotInput, ScreenshotOutput, ScreenshotResponse, SearchIconsInput, SearchIconsOutput, SnapshotId, SnapshotInput, SnapshotOutput, UngroupInput, UngroupOutput, UpdateElementsInput, UpdateElementsOutput, assertNever, bridgeSpawnFailed, bridgeUnreachable, catalogUnavailable, createNodeSkeletonExpander, deepEqual, describeThrown, diskConflict, emptySelection, expansionFailed, exportFailed, exportTimedOut, fail, frameIdOf, iconIdOf, idCollision, isFail, isOk, noBoundLabel, noBrowser, noScenes, notAPack, notARepo, ok, packInstallFailed, partition, pathOutsideRepo, point, portUnavailable, refusedFieldMessage, sceneFileMissing, staleVersion, storeUnavailable, unknownElement, unknownIcon, unknownScene, unknownSnapshot, unreadableSceneFile };
|
|
1499
|
+
//# sourceMappingURL=chunk-SHJT5BF3.js.map
|
|
1500
|
+
//# sourceMappingURL=chunk-SHJT5BF3.js.map
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { SceneElement, PackName, PackKind, IconId, SceneId, Scene, SnapshotId, SERVER_MANAGED_FIELDS, BINDING_FIELDS, GUIDE_TOPICS, DetailLevel, createNodeSkeletonExpander, ok, fail, bridgeSpawnFailed, bridgeUnreachable, GuideTopic, storeUnavailable, describeThrown, unreadableSceneFile, pathOutsideRepo, packInstallFailed, sceneFileMissing, notAPack, PackRecipe, portUnavailable, DescribeSceneOutput, DescribeSceneInput, GetSelectionOutput, GetSelectionInput, GetElementsOutput, GetElementsInput, ListScenesOutput, ListScenesInput, SceneSettingsOutput, SceneSettingsInput, SearchIconsOutput, SearchIconsInput, ScreenshotOutput, ScreenshotInput, ReadMeOutput, ReadMeInput, PlaceIconOutput, PlaceIconInput, SnapshotOutput, SnapshotInput, RestoreSnapshotOutput, RestoreSnapshotInput, ExportSceneOutput, ExportSceneInput, ImportSceneOutput, ImportSceneInput, ExportImageOutput, ExportImageInput, InstallPackOutput, InstallPackInput, notARepo, ScreenshotResponse, RemovePackOutput, ListPacksOutput, RawElementsOutput, FocusOutput, EmbedOutput, AnnotateOutput, FrameOutput, UngroupOutput, GroupOutput, ArrangeOutput, DeleteElementsOutput, UpdateElementsOutput, ConnectOutput, CreateElementsOutput, HealthResponse, iconIdOf, unknownScene, unknownSnapshot, exportFailed, unknownIcon, HOUSE, FileId, ElementId, diskConflict, refusedFieldMessage, idCollision, expansionFailed, emptySelection, unknownElement, frameIdOf, GroupId, staleVersion, deepEqual, exportTimedOut, noBrowser, DebugElementRequest, CreateElementsInput, ConnectToolInput, UpdateElementsInput, DeleteElementsInput, ArrangeToolInput, GroupInput, UngroupInput, FrameInput, AnnotateInput, EmbedInput, FocusInput, RawElementsInput, ListPacksInput, RemovePackInput, Index, ROUNDNESS, STALE_SELECTION_MS, noScenes, ClientMessage, assertNever, PROTOCOL_VERSION, ApiError, EXCALIDRAW_FILE_VERSION, EXCALIDRAW_FILE_TYPE,
|
|
2
|
+
import { SceneElement, PackName, PackKind, IconId, SceneId, Scene, SnapshotId, SERVER_MANAGED_FIELDS, BINDING_FIELDS, GUIDE_TOPICS, DetailLevel, createNodeSkeletonExpander, ok, fail, bridgeSpawnFailed, bridgeUnreachable, GuideTopic, storeUnavailable, describeThrown, unreadableSceneFile, pathOutsideRepo, packInstallFailed, sceneFileMissing, notAPack, PackRecipe, portUnavailable, DescribeSceneOutput, DescribeSceneInput, GetSelectionOutput, GetSelectionInput, GetElementsOutput, GetElementsInput, ListScenesOutput, ListScenesInput, SceneSettingsOutput, SceneSettingsInput, SearchIconsOutput, SearchIconsInput, ScreenshotOutput, ScreenshotInput, ReadMeOutput, ReadMeInput, PlaceIconOutput, PlaceIconInput, SnapshotOutput, SnapshotInput, RestoreSnapshotOutput, RestoreSnapshotInput, ExportSceneOutput, ExportSceneInput, ImportSceneOutput, ImportSceneInput, ExportImageOutput, ExportImageInput, InstallPackOutput, InstallPackInput, notARepo, ScreenshotResponse, RemovePackOutput, ListPacksOutput, RawElementsOutput, FocusOutput, EmbedOutput, AnnotateOutput, FrameOutput, UngroupOutput, GroupOutput, ArrangeOutput, DeleteElementsOutput, UpdateElementsOutput, ConnectOutput, CreateElementsOutput, HealthResponse, iconIdOf, unknownScene, unknownSnapshot, exportFailed, unknownIcon, HOUSE, FileId, ElementId, diskConflict, refusedFieldMessage, idCollision, expansionFailed, emptySelection, unknownElement, frameIdOf, GroupId, staleVersion, noBoundLabel, deepEqual, exportTimedOut, noBrowser, DebugElementRequest, CreateElementsInput, ConnectToolInput, UpdateElementsInput, DeleteElementsInput, ArrangeToolInput, GroupInput, UngroupInput, FrameInput, AnnotateInput, EmbedInput, FocusInput, RawElementsInput, ListPacksInput, RemovePackInput, Index, ROUNDNESS, STALE_SELECTION_MS, noScenes, ClientMessage, assertNever, PROTOCOL_VERSION, ApiError, EXCALIDRAW_FILE_VERSION, EXCALIDRAW_FILE_TYPE, point, FLAG_GLYPH, KIND_ABBREVIATION, catalogUnavailable, FrameId, IconItem, ExcalidrawFile } from './chunk-SHJT5BF3.js';
|
|
3
3
|
import './chunk-7D4SUZUM.js';
|
|
4
4
|
import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
@@ -1049,6 +1049,7 @@ var KNOWN_CODES = /* @__PURE__ */ new Set([
|
|
|
1049
1049
|
"STALE_VERSION",
|
|
1050
1050
|
"ID_COLLISION",
|
|
1051
1051
|
"UNKNOWN_ELEMENT",
|
|
1052
|
+
"NO_BOUND_LABEL",
|
|
1052
1053
|
"EMPTY_SELECTION",
|
|
1053
1054
|
"DISK_CONFLICT",
|
|
1054
1055
|
"UNREADABLE_SCENE_FILE",
|
|
@@ -3237,8 +3238,42 @@ function edgePoint(from, to, gap) {
|
|
|
3237
3238
|
const t = scale + push;
|
|
3238
3239
|
return [cx + dx * t, cy + dy * t];
|
|
3239
3240
|
}
|
|
3240
|
-
function
|
|
3241
|
+
function redrawOne(current, labels, arrow) {
|
|
3242
|
+
if (arrow.type !== "arrow") return [];
|
|
3243
|
+
const fromId = startOf(arrow);
|
|
3244
|
+
const toId = endOf(arrow);
|
|
3245
|
+
if (fromId === null || toId === null) return [];
|
|
3246
|
+
const from = current.get(fromId);
|
|
3247
|
+
const to = current.get(toId);
|
|
3248
|
+
if (from === void 0 || to === void 0) return [];
|
|
3249
|
+
const startGap = arrow.startBinding?.gap ?? 4;
|
|
3250
|
+
const endGap = arrow.endBinding?.gap ?? 4;
|
|
3251
|
+
const [sx, sy] = edgePoint(boxOf(from), boxOf(to), startGap);
|
|
3252
|
+
const [ex, ey] = edgePoint(boxOf(to), boxOf(from), endGap);
|
|
3253
|
+
const points = [point(0, 0), point(ex - sx, ey - sy)];
|
|
3254
|
+
const redrawn = {
|
|
3255
|
+
...arrow,
|
|
3256
|
+
x: sx,
|
|
3257
|
+
y: sy,
|
|
3258
|
+
width: Math.abs(ex - sx),
|
|
3259
|
+
height: Math.abs(ey - sy),
|
|
3260
|
+
points,
|
|
3261
|
+
lastCommittedPoint: null
|
|
3262
|
+
};
|
|
3263
|
+
const label = labels.get(arrow.id);
|
|
3264
|
+
if (label === void 0) return [redrawn];
|
|
3265
|
+
return [
|
|
3266
|
+
redrawn,
|
|
3267
|
+
{
|
|
3268
|
+
...label,
|
|
3269
|
+
x: (sx + ex) / 2 - label.width / 2,
|
|
3270
|
+
y: (sy + ey) / 2 - label.height / 2
|
|
3271
|
+
}
|
|
3272
|
+
];
|
|
3273
|
+
}
|
|
3274
|
+
function redrawArrowsTouching(scene, moved) {
|
|
3241
3275
|
const current = new Map(scene.map((element) => [element.id, moved.get(element.id) ?? element]));
|
|
3276
|
+
const labels = labelsByContainer([...current.values()]);
|
|
3242
3277
|
const redrawn = [];
|
|
3243
3278
|
for (const element of current.values()) {
|
|
3244
3279
|
if (element.type !== "arrow") continue;
|
|
@@ -3246,26 +3281,16 @@ function rebindArrows(scene, moved) {
|
|
|
3246
3281
|
const toId = endOf(element);
|
|
3247
3282
|
if (fromId === null || toId === null) continue;
|
|
3248
3283
|
if (!moved.has(fromId) && !moved.has(toId)) continue;
|
|
3249
|
-
|
|
3250
|
-
const to = current.get(toId);
|
|
3251
|
-
if (from === void 0 || to === void 0) continue;
|
|
3252
|
-
const startGap = element.startBinding?.gap ?? 4;
|
|
3253
|
-
const endGap = element.endBinding?.gap ?? 4;
|
|
3254
|
-
const [sx, sy] = edgePoint(boxOf(from), boxOf(to), startGap);
|
|
3255
|
-
const [ex, ey] = edgePoint(boxOf(to), boxOf(from), endGap);
|
|
3256
|
-
const points = [point(0, 0), point(ex - sx, ey - sy)];
|
|
3257
|
-
redrawn.push({
|
|
3258
|
-
...element,
|
|
3259
|
-
x: sx,
|
|
3260
|
-
y: sy,
|
|
3261
|
-
width: Math.abs(ex - sx),
|
|
3262
|
-
height: Math.abs(ey - sy),
|
|
3263
|
-
points,
|
|
3264
|
-
lastCommittedPoint: null
|
|
3265
|
-
});
|
|
3284
|
+
redrawn.push(...redrawOne(current, labels, element));
|
|
3266
3285
|
}
|
|
3267
3286
|
return redrawn;
|
|
3268
3287
|
}
|
|
3288
|
+
function redrawArrows(scene, ids) {
|
|
3289
|
+
const current = new Map(scene.map((element) => [element.id, element]));
|
|
3290
|
+
const labels = labelsByContainer(scene);
|
|
3291
|
+
const wanted = new Set(ids);
|
|
3292
|
+
return [...current.values()].filter((element) => wanted.has(element.id)).flatMap((element) => redrawOne(current, labels, element));
|
|
3293
|
+
}
|
|
3269
3294
|
function translate(scene, deltas) {
|
|
3270
3295
|
const labels = labelsByContainer(scene);
|
|
3271
3296
|
const moved = /* @__PURE__ */ new Map();
|
|
@@ -3280,7 +3305,7 @@ function translate(scene, deltas) {
|
|
|
3280
3305
|
moved.set(label.id, { ...label, x: label.x + dx, y: label.y + dy });
|
|
3281
3306
|
}
|
|
3282
3307
|
}
|
|
3283
|
-
return [...moved.values(), ...
|
|
3308
|
+
return [...moved.values(), ...redrawArrowsTouching(scene, moved)];
|
|
3284
3309
|
}
|
|
3285
3310
|
var alignments = {
|
|
3286
3311
|
"align-left": (box, bounds) => [bounds.x - box.x, 0],
|
|
@@ -3576,7 +3601,15 @@ function connectElements(deps) {
|
|
|
3576
3601
|
};
|
|
3577
3602
|
const created = await deps.write.create(scene.value, [skeleton], true);
|
|
3578
3603
|
if (!created.ok) return created;
|
|
3579
|
-
const
|
|
3604
|
+
const placed = redrawArrows(
|
|
3605
|
+
[...scene.value.elements, ...created.value.created, ...created.value.changed],
|
|
3606
|
+
[skeleton.id]
|
|
3607
|
+
);
|
|
3608
|
+
const geometry = new Map(placed.map((element) => [element.id, element]));
|
|
3609
|
+
const committed = deps.write.append(scene.value, {
|
|
3610
|
+
created: created.value.created.map((element) => geometry.get(element.id) ?? element),
|
|
3611
|
+
changed: created.value.changed
|
|
3612
|
+
});
|
|
3580
3613
|
if (!committed.ok) return committed;
|
|
3581
3614
|
return ok({ ...committed.value, arrowId: skeleton.id });
|
|
3582
3615
|
};
|
|
@@ -4195,6 +4228,13 @@ function rawElements(deps) {
|
|
|
4195
4228
|
|
|
4196
4229
|
// src/app/usecases/write/update-elements.ts
|
|
4197
4230
|
var missing = (id) => toRejection(id, unknownElement(id));
|
|
4231
|
+
var onlyRequested = (landed, requested) => {
|
|
4232
|
+
const asked = new Set(requested);
|
|
4233
|
+
return landed.filter((id) => asked.has(id));
|
|
4234
|
+
};
|
|
4235
|
+
var captionOf = (scene, container) => scene.find(
|
|
4236
|
+
(element) => element.type === "text" && element.containerId === container && !element.isDeleted
|
|
4237
|
+
);
|
|
4198
4238
|
function updateElements(deps) {
|
|
4199
4239
|
return (input) => {
|
|
4200
4240
|
const scene = deps.write.resolve(input.sceneId);
|
|
@@ -4203,25 +4243,73 @@ function updateElements(deps) {
|
|
|
4203
4243
|
const changed = [];
|
|
4204
4244
|
const rejected = [];
|
|
4205
4245
|
const expectations = [];
|
|
4246
|
+
const moved = /* @__PURE__ */ new Map();
|
|
4247
|
+
const knockOn = [];
|
|
4206
4248
|
for (const update of input.updates) {
|
|
4207
4249
|
const existing = byId.get(update.id);
|
|
4208
4250
|
if (existing === void 0 || existing.isDeleted) {
|
|
4209
4251
|
rejected.push(missing(update.id));
|
|
4210
4252
|
continue;
|
|
4211
4253
|
}
|
|
4254
|
+
const { role, label, ...rest } = update.patch;
|
|
4212
4255
|
const patch = Object.fromEntries(
|
|
4213
|
-
Object.entries(
|
|
4256
|
+
Object.entries(rest).filter(([, value]) => value !== void 0)
|
|
4214
4257
|
);
|
|
4215
|
-
|
|
4258
|
+
if (label !== void 0) {
|
|
4259
|
+
const caption = captionOf(scene.value.elements, update.id);
|
|
4260
|
+
if (caption === void 0) {
|
|
4261
|
+
rejected.push(toRejection(update.id, noBoundLabel(update.id)));
|
|
4262
|
+
continue;
|
|
4263
|
+
}
|
|
4264
|
+
knockOn.push({ ...caption, text: label, originalText: label });
|
|
4265
|
+
}
|
|
4266
|
+
const next = {
|
|
4267
|
+
...existing,
|
|
4268
|
+
...role === void 0 ? {} : styleForRole(role),
|
|
4269
|
+
...patch
|
|
4270
|
+
};
|
|
4271
|
+
changed.push(next);
|
|
4216
4272
|
expectations.push({ id: update.id, expectedVersion: update.expectedVersion });
|
|
4273
|
+
const dx = next.x - existing.x;
|
|
4274
|
+
const dy = next.y - existing.y;
|
|
4275
|
+
if (dx !== 0 || dy !== 0) moved.set(update.id, next);
|
|
4276
|
+
else if (next.width !== existing.width || next.height !== existing.height) {
|
|
4277
|
+
moved.set(update.id, next);
|
|
4278
|
+
}
|
|
4217
4279
|
}
|
|
4218
|
-
|
|
4280
|
+
if (moved.size > 0) {
|
|
4281
|
+
for (const [id, next] of moved) {
|
|
4282
|
+
const before = byId.get(id);
|
|
4283
|
+
const caption = captionOf(scene.value.elements, id);
|
|
4284
|
+
if (before === void 0 || caption === void 0) continue;
|
|
4285
|
+
knockOn.push({
|
|
4286
|
+
...caption,
|
|
4287
|
+
x: caption.x + (next.x - before.x),
|
|
4288
|
+
y: caption.y + (next.y - before.y)
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
knockOn.push(...redrawArrowsTouching(scene.value.elements, moved));
|
|
4292
|
+
}
|
|
4293
|
+
for (const element of knockOn) {
|
|
4294
|
+
const before = byId.get(element.id);
|
|
4295
|
+
if (before !== void 0) {
|
|
4296
|
+
expectations.push({ id: element.id, expectedVersion: before.version });
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
const applied = deps.write.apply(scene.value, [...changed, ...knockOn], { expectations });
|
|
4219
4300
|
if (!applied.ok) return applied;
|
|
4220
4301
|
return ok({
|
|
4221
4302
|
sceneId: applied.value.sceneId,
|
|
4222
4303
|
sceneVersion: applied.value.sceneVersion,
|
|
4223
4304
|
broadcastTo: applied.value.broadcastTo,
|
|
4224
|
-
|
|
4305
|
+
// What the caller asked for, not everything that moved. A caption that followed its
|
|
4306
|
+
// box and an arrow that was redrawn are consequences; listing them would turn this
|
|
4307
|
+
// into "every element that changed" and leave the caller working out which of them
|
|
4308
|
+
// it had actually requested.
|
|
4309
|
+
applied: onlyRequested(
|
|
4310
|
+
applied.value.applied,
|
|
4311
|
+
input.updates.map((update) => update.id)
|
|
4312
|
+
),
|
|
4225
4313
|
rejected: [...rejected, ...applied.value.rejected]
|
|
4226
4314
|
});
|
|
4227
4315
|
};
|
|
@@ -4255,6 +4343,11 @@ function deleteElements(deps) {
|
|
|
4255
4343
|
id: deletion.id,
|
|
4256
4344
|
expectedVersion: deletion.expectedVersion ?? existing.version
|
|
4257
4345
|
});
|
|
4346
|
+
const caption = captionOf(scene.value.elements, deletion.id);
|
|
4347
|
+
if (caption !== void 0) {
|
|
4348
|
+
changed.push({ ...caption, isDeleted: true });
|
|
4349
|
+
expectations.push({ id: caption.id, expectedVersion: caption.version });
|
|
4350
|
+
}
|
|
4258
4351
|
}
|
|
4259
4352
|
const applied = deps.write.apply(scene.value, changed, { expectations });
|
|
4260
4353
|
if (!applied.ok) return applied;
|
|
@@ -4262,7 +4355,10 @@ function deleteElements(deps) {
|
|
|
4262
4355
|
sceneId: applied.value.sceneId,
|
|
4263
4356
|
sceneVersion: applied.value.sceneVersion,
|
|
4264
4357
|
broadcastTo: applied.value.broadcastTo,
|
|
4265
|
-
deleted:
|
|
4358
|
+
deleted: onlyRequested(
|
|
4359
|
+
applied.value.applied,
|
|
4360
|
+
input.deletions.map((deletion) => deletion.id)
|
|
4361
|
+
),
|
|
4266
4362
|
rejected: [...rejected, ...applied.value.rejected]
|
|
4267
4363
|
});
|
|
4268
4364
|
};
|
|
@@ -4305,7 +4401,7 @@ function readEnv(source) {
|
|
|
4305
4401
|
}
|
|
4306
4402
|
|
|
4307
4403
|
// src/composition/container.ts
|
|
4308
|
-
var PACKAGE_VERSION = "1.0.
|
|
4404
|
+
var PACKAGE_VERSION = "1.0.1";
|
|
4309
4405
|
function readRuntime() {
|
|
4310
4406
|
return {
|
|
4311
4407
|
env: process.env,
|
|
@@ -5613,6 +5709,8 @@ var STATUS = {
|
|
|
5613
5709
|
ID_COLLISION: 409,
|
|
5614
5710
|
// 404, not 409: the id is not there to conflict with.
|
|
5615
5711
|
UNKNOWN_ELEMENT: 404,
|
|
5712
|
+
// 422: the id is fine and the request is well-formed, there is simply nothing to rename.
|
|
5713
|
+
NO_BOUND_LABEL: 422,
|
|
5616
5714
|
EMPTY_SELECTION: 400,
|
|
5617
5715
|
DISK_CONFLICT: 409,
|
|
5618
5716
|
UNREADABLE_SCENE_FILE: 422,
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createNodeSkeletonExpander, fail, isFail, isOk, ok, partition } from './chunk-
|
|
1
|
+
export { createNodeSkeletonExpander, fail, isFail, isOk, ok, partition } from './chunk-SHJT5BF3.js';
|
|
2
2
|
import './chunk-7D4SUZUM.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chalkbridge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted Excalidraw canvas for Claude Code to draw and reason on architecture diagrams.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"ui-dist",
|
|
23
23
|
"guides",
|
|
24
24
|
"packs",
|
|
25
|
+
"CHANGELOG.md",
|
|
25
26
|
"THIRD-PARTY-NOTICES.md"
|
|
26
27
|
],
|
|
27
28
|
"bin": {
|