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 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
- raw = loaded.value([...context ?? [], ...skeletons], { regenerateIds: false });
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-HFKCLTVU.js.map
1494
- //# sourceMappingURL=chunk-HFKCLTVU.js.map
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, FLAG_GLYPH, point, KIND_ABBREVIATION, catalogUnavailable, FrameId, IconItem, ExcalidrawFile } from './chunk-HFKCLTVU.js';
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 rebindArrows(scene, moved) {
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
- const from = current.get(fromId);
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(), ...rebindArrows(scene, moved)];
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 committed = deps.write.append(scene.value, created.value);
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(update.patch).filter(([, value]) => value !== void 0)
4256
+ Object.entries(rest).filter(([, value]) => value !== void 0)
4214
4257
  );
4215
- changed.push({ ...existing, ...patch });
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
- const applied = deps.write.apply(scene.value, changed, { expectations });
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
- applied: applied.value.applied,
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: applied.value.applied,
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.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-HFKCLTVU.js';
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.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": {