@creator-notes/cnotes 0.26.0 → 0.30.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 CHANGED
@@ -36,14 +36,13 @@ cnotes search semantic "onboarding flow"
36
36
  | `cnotes versions list\|create` | `cnotes v` | Note version history |
37
37
  | `cnotes canvas list\|get\|create\|delete` | `cnotes c` | Manage canvases |
38
38
  | `cnotes canvas place` | | Declarative layout — server packs items with no overlap |
39
- | `cnotes canvas add-node\|add-text\|add-richtext\|add-list\|add-link` | | Add a single node at explicit `--x`/`--y` |
39
+ | `cnotes canvas add-node\|add-text\|add-list\|add-link` | | Add a single node at explicit `--x`/`--y` (`add-richtext` is a deprecated alias of `add-text`) |
40
40
  | `cnotes canvas bulk-add\|bulk-move\|bulk-remove` | | Bulk canvas operations — requires explicit coordinates |
41
41
  | `cnotes canvas from-template` | | Create canvas from template |
42
42
  | `cnotes search semantic` | `cnotes s` | Semantic search across notes |
43
43
  | `cnotes timeline` | `cnotes tl` | Recent activity feed |
44
44
  | `cnotes relationships list` | `cnotes rel` | Note relationships |
45
45
  | `cnotes types list\|create\|update` | `cnotes t` | Manage note types (supertags) |
46
- | `cnotes memory query\|facts\|entities` | `cnotes mem` | Query workspace knowledge |
47
46
  | `cnotes workspace list\|select\|current` | | Workspace management |
48
47
  | `cnotes config get-server\|set-server` | | CLI configuration |
49
48
  | `cnotes mcp setup` | | Print MCP server config for Claude |
@@ -171,7 +170,7 @@ cnotes canvas agent-run wrap \
171
170
  --canvas <canvasId> \
172
171
  --prompt "Reorganize roadmap into Q1/Q2 clusters" \
173
172
  -- bash -c '
174
- cnotes canvas add-text "$CANVAS" --text "Q1" --x 100 --y 100
173
+ cnotes canvas add-text "$CANVAS" --content "# Q1" --x 100 --y 100
175
174
  cnotes canvas add-edge "$CANVAS" --source <id1> --target <id2>
176
175
  '
177
176
  ```
@@ -187,7 +186,7 @@ Four building blocks:
187
186
  - **`stack`** — items flow along one axis (`vertical` or `horizontal`), like CSS flexbox
188
187
  - **`grid`** — N columns, items wrap to new rows, like CSS grid auto-flow
189
188
  - **`anchor`** — place a sub-tree relative to an existing canvas node
190
- - **`item`** — a leaf: `note`, `text`, `richtext`, `list`, or `canvas` (link)
189
+ - **`item`** — a leaf: `note`, `richtext` (the Text element — a markdown heading with no tint IS a heading), `list`, `canvas` (link), or `section` (frame)
191
190
 
192
191
  Example — a richtext header above a 3-column grid of notes:
193
192
 
package/dist/cn.js CHANGED
@@ -17,7 +17,6 @@ import { registerRelationshipsCommands } from "./commands/relationships.js";
17
17
  import { registerSearchCommands } from "./commands/search.js";
18
18
  import { registerTimelineCommands } from "./commands/timeline.js";
19
19
  import { registerTypesCommands } from "./commands/types.js";
20
- import { registerMemoryCommands } from "./commands/memory.js";
21
20
  import { registerFilesCommands } from "./commands/files.js";
22
21
  import { registerMcpCommands } from "./commands/mcp.js";
23
22
  import { registerInitCommand } from "./commands/init.js";
@@ -69,7 +68,6 @@ registerRelationshipsCommands(program);
69
68
  registerSearchCommands(program);
70
69
  registerTimelineCommands(program);
71
70
  registerTypesCommands(program);
72
- registerMemoryCommands(program);
73
71
  registerFilesCommands(program);
74
72
  registerMcpCommands(program);
75
73
  registerSchemaCommand(program);
package/dist/cn.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cn.js","sourceRoot":"","sources":["../src/cn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cn.js","sourceRoot":"","sources":["../src/cn.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/commands/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAwF5C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA44D7D"}
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../src/commands/canvas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAkG5C,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiwE7D"}
@@ -657,55 +657,19 @@ Examples:
657
657
  handleError(error, cfg.json);
658
658
  }
659
659
  });
660
- canvas
661
- .command("add-text")
662
- .description("Add a text annotation to a canvas — one-line section labels / wayfinding only; content belongs in notes")
663
- .argument("<canvasId>", "Canvas ID")
664
- .requiredOption("--text <content>", "Text content")
665
- .addOption(new Option("--size <size>", "Text size: heading (default, larger) or paragraph (smaller readable text)").choices(["heading", "paragraph"]).default("heading"))
666
- .addOption(new Option("--color <variant>", "Color variant: normal (heading color), muted (body text color), highlighted (accent)").choices(["normal", "muted", "highlighted"]))
667
- .option("--x <n>", "X position", "100")
668
- .option("--y <n>", "Y position", "100")
669
- .addHelpText("after", `
670
- Examples:
671
- $ cnotes canvas add-text <canvasId> --text "Section A" --size heading
672
- $ cnotes canvas add-text <canvasId> --text "Note body" --size paragraph --color muted
673
- `)
674
- .action(async (canvasId, opts) => {
675
- const cfg = resolveConfig(program.opts());
676
- const api = new ApiClient(cfg.serverUrl, cfg.token);
677
- const fontSize = opts.size === "paragraph" ? 18 : 32;
678
- try {
679
- const data = await api.post(`/api/canvas/${canvasId}`, {
680
- action: "addTextNode",
681
- content: opts.text,
682
- fontSize,
683
- colorVariant: opts.color,
684
- positionX: parseInt(opts.x, 10),
685
- positionY: parseInt(opts.y, 10),
686
- });
687
- if (cfg.json) {
688
- outputJson(data);
689
- }
690
- else {
691
- console.log(`Added text node to canvas at (${opts.x}, ${opts.y})`);
692
- }
693
- }
694
- catch (error) {
695
- handleError(error, cfg.json);
696
- }
697
- });
698
- canvas
699
- .command("add-richtext")
700
- .description("Add a richtext sticky to a canvas: orientation banners, emphasis quoting a note, image tiles. Sticky-sized (≤ ~3 sentences) — richtext is unversioned and unsearchable, so knowledge worth keeping goes in a typed note. Plain markdown only: NO [NOTE-X](relationship:...) mention syntax (corrupts the card)")
701
- .argument("<canvasId>", "Canvas ID")
702
- .option("--content <content>", "Richtext content inline (markdown; plain display IDs only, no mention syntax)")
703
- .option("--content-file <path>", "Read richtext content (markdown) from a file")
704
- .addOption(new Option("--size <size>", "Display size: small (560px), medium (1120px), large (1680px billboard)").choices(["small", "medium", "large"]))
705
- .option("--color <hex>", "Background color hex — omit unless it encodes a role used consistently on this canvas; never pastels")
706
- .option("--x <n>", "X position", "100")
707
- .option("--y <n>", "Y position", "100")
708
- .action(async (canvasId, opts) => {
660
+ // --- Text element (wire type `richtext`) ---
661
+ //
662
+ // The canvas has ONE free-form text element, called "Text", and it is a richtext
663
+ // node. The old `text` node kind (the "Heading": plain string + fontSize) is LEGACY:
664
+ // existing ones still render, move and delete, but nothing creates them any more. A
665
+ // Text element with no background tint and `# Foo` as its content IS a heading, which
666
+ // is what made the old kind redundant.
667
+ //
668
+ // `add-text` and `add-richtext` are the same command (add-richtext is the deprecated
669
+ // alias); both post `addRichtextNode`. The old `add-text` flags (--text, --size
670
+ // heading|paragraph, --color normal|muted|highlighted) are gone on purpose — an old
671
+ // invocation must fail loudly rather than be silently reinterpreted.
672
+ const addTextElementAction = async (canvasId, opts) => {
709
673
  const cfg = resolveConfig(program.opts());
710
674
  const api = new ApiClient(cfg.serverUrl, cfg.token);
711
675
  let content;
@@ -719,12 +683,30 @@ Examples:
719
683
  handleError(new ValidationError("Either --content or --content-file is required"), cfg.json);
720
684
  return;
721
685
  }
686
+ // --color is a background tint HEX. Reject anything else so the retired heading
687
+ // flags (--color normal|muted|highlighted) fail loudly instead of being posted
688
+ // through as a nonsense colorHex.
689
+ //
690
+ // This must accept exactly what the server accepts — #RGB or #RRGGBB, per
691
+ // HEX_COLOR_REGEX in convex/canvas.ts. The create mutation does NOT re-validate
692
+ // colorHex (only the update paths do), so anything we let through is stored raw:
693
+ // a hex without the leading `#` is not a valid CSS color, so the tint would
694
+ // silently not render AND the node would then fail every later style update.
695
+ // Hence: allow a bare hex for convenience, but normalize the `#` on before POST.
696
+ let colorHex;
697
+ if (opts.color) {
698
+ if (!/^#?(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(opts.color)) {
699
+ handleError(new ValidationError(`--color must be a 3- or 6-digit hex color (e.g. '#c2410c'), got '${opts.color}'. There are no named color variants — a text element has no background unless you pass a hex.`), cfg.json);
700
+ return;
701
+ }
702
+ colorHex = opts.color.startsWith("#") ? opts.color : `#${opts.color}`;
703
+ }
722
704
  try {
723
705
  const data = await api.post(`/api/canvas/${canvasId}`, {
724
706
  action: "addRichtextNode",
725
707
  content,
726
708
  size: opts.size,
727
- colorHex: opts.color,
709
+ colorHex,
728
710
  positionX: parseInt(opts.x, 10),
729
711
  positionY: parseInt(opts.y, 10),
730
712
  });
@@ -733,13 +715,32 @@ Examples:
733
715
  }
734
716
  else {
735
717
  const heightInfo = data.estimatedHeight ? ` — estimated height: ${data.estimatedHeight}px` : '';
736
- console.log(`Added richtext node to canvas at (${opts.x}, ${opts.y})${heightInfo}`);
718
+ console.log(`Added text element to canvas at (${opts.x}, ${opts.y})${heightInfo}`);
737
719
  }
738
720
  }
739
721
  catch (error) {
740
722
  handleError(error, cfg.json);
741
723
  }
742
- });
724
+ };
725
+ const defineAddTextCommand = (name, description) => canvas
726
+ .command(name)
727
+ .description(description)
728
+ .argument("<canvasId>", "Canvas ID")
729
+ .option("--content <content>", "Text content inline (markdown — headings, bold, links; plain display IDs only, no mention syntax)")
730
+ .option("--content-file <path>", "Read text content (markdown) from a file")
731
+ .addOption(new Option("--size <size>", "Display size: small (560px), medium (1120px), large (1680px billboard)").choices(["small", "medium", "large"]))
732
+ .option("--color <hex>", "Background tint hex — DEFAULT IS NO BACKGROUND; omit unless the tint encodes a role used consistently on this canvas; never pastels")
733
+ .option("--x <n>", "X position", "100")
734
+ .option("--y <n>", "Y position", "100")
735
+ .addHelpText("after", `
736
+ Examples:
737
+ $ cnotes canvas add-text <canvasId> --content '# Q1 Goals' # a heading: markdown h1, untinted
738
+ $ cnotes canvas add-text <canvasId> --content-file ./banner.md --size medium # orientation banner
739
+ $ cnotes canvas add-text <canvasId> --content 'Read left to right.' --color '#c2410c'
740
+ `)
741
+ .action(addTextElementAction);
742
+ defineAddTextCommand("add-text", "Add a Text element to a canvas — free-form rich text placed on the canvas (markdown: headings, emphasis, images), with an optional background tint (none by default). A heading is just `--content '# Title'`. Keep it card-sized (≤ ~3 sentences): text elements are unversioned and unsearchable, so knowledge worth keeping goes in a typed note. Plain markdown only: NO [NOTE-X](relationship:...) mention syntax (corrupts the card)");
743
+ defineAddTextCommand("add-richtext", "[DEPRECATED — use `add-text`] Alias of `add-text`: adds the same Text element (richtext) with the same flags. Kept for existing scripts; prefer `add-text`.");
743
744
  canvas
744
745
  .command("add-section")
745
746
  .description("Add a section frame — a named background region (Miro-style). Whatever sits inside moves with the frame when dragged; notes inside stay individually draggable. Mints a SECTION-<n> id: mention it from notes as [[SECTION-<n>]] to deep-link readers to that region, so name frames like places (\"Shaping\", \"Open Questions\"), not sentences. With --notes the frame is created WITH those notes laid out inside it (collision-aware, no coordinate math — the preferred way to build a populated section).")
@@ -1112,11 +1113,217 @@ Placement (relative to nodes already on the canvas):
1112
1113
  handleError(error, cfg.json);
1113
1114
  }
1114
1115
  });
1116
+ canvas
1117
+ .command("arrange")
1118
+ .description("Reflow EXISTING canvas nodes with the layout solver (no coordinate math). " +
1119
+ "Unlike `place` (create-only), this MOVES nodes already on the canvas — " +
1120
+ "mixed kinds welcome (note, canvas-link, richtext, list). With --section, " +
1121
+ "packs them into that frame and auto-resizes it in place (SECTION-<n> stays stable).")
1122
+ .argument("<canvasId>", "Canvas ID")
1123
+ .option("--section <id>", "Target SECTION-<n> (or raw id) to pack into; auto-resizes the frame around the new layout")
1124
+ .option("--nodes <json>", 'JSON array of {nodeId, nodeType} to rearrange, e.g. \'[{"nodeId":"…","nodeType":"canvas"}]\'')
1125
+ .option("--nodes-stdin", "Read the --nodes JSON array from stdin")
1126
+ .option("--layout <mode>", "Packing mode when using --nodes: grid (default) or stack", "grid")
1127
+ .option("--columns <n>", "Grid columns when --layout grid", "3")
1128
+ .option("--gap <preset>", "Gap preset or raw px: tight | medium | spacious | <number>", "medium")
1129
+ .option("--stack-axis <axis>", "Axis when --layout stack: vertical | horizontal", "vertical")
1130
+ .option("--no-auto-resize", "With --section: move nodes but leave the frame bounds unchanged")
1131
+ .option("--spec <file>", "Full layout document JSON (type: \"existing\" leaves). Alternative to --nodes")
1132
+ .option("--spec-inline <json>", "Full layout document as a JSON string")
1133
+ .option("--spec-stdin", "Read a full layout document from stdin")
1134
+ .option("--dry-run", "Solve and preview positions without moving anything")
1135
+ .addHelpText("after", `
1136
+ Examples:
1137
+ $ cnotes canvas arrange CANVAS-265 --section SECTION-90 \\
1138
+ --nodes '[{"nodeId":"abc","nodeType":"canvas"},{"nodeId":"NOTE-3","nodeType":"note"}]' \\
1139
+ --layout grid --columns 4 --gap medium
1140
+ $ cnotes canvas arrange CANVAS-265 --spec ./reflow.json --section SECTION-90
1141
+ `)
1142
+ .action(async (canvasId, opts) => {
1143
+ const cfg = resolveConfig(program.opts());
1144
+ const api = new ApiClient(cfg.serverUrl, cfg.token);
1145
+ try {
1146
+ const validNodeTypes = new Set([
1147
+ "note",
1148
+ "text",
1149
+ "list",
1150
+ "canvas",
1151
+ "richtext",
1152
+ ]);
1153
+ const specMode = Boolean(opts.spec || opts.specInline || opts.specStdin);
1154
+ const nodesMode = Boolean(opts.nodes || opts.nodesStdin);
1155
+ if (specMode && nodesMode) {
1156
+ throw new ValidationError("Use either --nodes / --nodes-stdin OR a full --spec, not both");
1157
+ }
1158
+ if (!specMode && !nodesMode) {
1159
+ throw new ValidationError("Provide --nodes (or --nodes-stdin), or a full --spec / --spec-inline / --spec-stdin");
1160
+ }
1161
+ let doc;
1162
+ if (specMode) {
1163
+ let raw;
1164
+ if (opts.spec)
1165
+ raw = readFileSafe(opts.spec);
1166
+ else if (opts.specInline)
1167
+ raw = opts.specInline;
1168
+ else {
1169
+ raw = await new Promise((resolve, reject) => {
1170
+ const chunks = [];
1171
+ process.stdin.on("data", (c) => chunks.push(c));
1172
+ process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
1173
+ process.stdin.on("error", reject);
1174
+ });
1175
+ }
1176
+ let parsed;
1177
+ try {
1178
+ parsed = JSON.parse(raw);
1179
+ }
1180
+ catch (e) {
1181
+ throw new ValidationError(`Layout spec is not valid JSON: ${e instanceof Error ? e.message : String(e)}`);
1182
+ }
1183
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1184
+ throw new ValidationError("Layout spec must be a JSON object");
1185
+ }
1186
+ doc = parsed;
1187
+ }
1188
+ else {
1189
+ let nodesRaw;
1190
+ if (opts.nodesStdin) {
1191
+ nodesRaw = await new Promise((resolve, reject) => {
1192
+ const chunks = [];
1193
+ process.stdin.on("data", (c) => chunks.push(c));
1194
+ process.stdin.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
1195
+ process.stdin.on("error", reject);
1196
+ });
1197
+ }
1198
+ else {
1199
+ nodesRaw = opts.nodes;
1200
+ }
1201
+ const nodes = parseJsonOption(nodesRaw, "--nodes", '[{"nodeId":"abc","nodeType":"canvas"}]');
1202
+ if (!Array.isArray(nodes) || nodes.length === 0) {
1203
+ throw new ValidationError("--nodes must be a non-empty JSON array");
1204
+ }
1205
+ const items = nodes.map((n, i) => {
1206
+ if (!n || typeof n.nodeId !== "string" || !n.nodeId.trim()) {
1207
+ throw new ValidationError(`Item ${i}: nodeId is required`);
1208
+ }
1209
+ const nodeType = n.nodeType || "note";
1210
+ if (!validNodeTypes.has(nodeType)) {
1211
+ throw new ValidationError(`Item ${i}: nodeType must be one of: note, text, list, canvas, richtext`);
1212
+ }
1213
+ return {
1214
+ kind: "item",
1215
+ type: "existing",
1216
+ nodeId: n.nodeId.trim(),
1217
+ nodeType,
1218
+ };
1219
+ });
1220
+ const layout = opts.layout || "grid";
1221
+ if (layout !== "grid" && layout !== "stack") {
1222
+ throw new ValidationError('--layout must be "grid" or "stack"');
1223
+ }
1224
+ let gap = opts.gap ?? "medium";
1225
+ if (gap !== "tight" && gap !== "medium" && gap !== "spacious") {
1226
+ const asNum = Number(gap);
1227
+ if (!Number.isFinite(asNum) || asNum < 0) {
1228
+ throw new ValidationError('--gap must be tight, medium, spacious, or a non-negative number');
1229
+ }
1230
+ gap = asNum;
1231
+ }
1232
+ let child;
1233
+ if (layout === "stack") {
1234
+ const axis = opts.stackAxis || "vertical";
1235
+ if (axis !== "vertical" && axis !== "horizontal") {
1236
+ throw new ValidationError('--stack-axis must be "vertical" or "horizontal"');
1237
+ }
1238
+ child = { kind: "stack", axis, gap, items };
1239
+ }
1240
+ else {
1241
+ const columns = Math.max(1, parseInt(opts.columns, 10) || 3);
1242
+ child = { kind: "grid", columns, gap, items };
1243
+ }
1244
+ if (opts.section) {
1245
+ // Name is required by the section container schema; the server
1246
+ // updates the existing frame in place and keeps SECTION-<n>.
1247
+ // Fetch the current name so we don't invent a placeholder title.
1248
+ const canvas = await api.get(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`);
1249
+ const want = String(opts.section);
1250
+ const wantUpper = want.toUpperCase();
1251
+ const section = (canvas.sectionNodes ?? []).find((s) => s.id === want ||
1252
+ (s.displayId && s.displayId.toUpperCase() === wantUpper));
1253
+ if (!section) {
1254
+ throw new ValidationError(`Section "${opts.section}" not found on this canvas`);
1255
+ }
1256
+ doc = {
1257
+ root: {
1258
+ kind: "section",
1259
+ name: section.name,
1260
+ key: "frame",
1261
+ child,
1262
+ },
1263
+ };
1264
+ }
1265
+ else {
1266
+ doc = { root: child };
1267
+ }
1268
+ }
1269
+ const autoResize = opts.autoResize !== false;
1270
+ const data = await api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`, {
1271
+ action: "arrangeLayout",
1272
+ doc,
1273
+ ...(opts.section ? { targetSectionId: opts.section } : {}),
1274
+ autoResize,
1275
+ ...(opts.dryRun ? { dryRun: true } : {}),
1276
+ });
1277
+ const link = opts.dryRun
1278
+ ? null
1279
+ : canvasLinkForRef(cfg.serverUrl, cfg.workspaceId, canvasId);
1280
+ if (cfg.json) {
1281
+ if (link)
1282
+ data.url = link;
1283
+ outputJson(data);
1284
+ return;
1285
+ }
1286
+ const items = (data.items || []);
1287
+ const bbox = data.bbox;
1288
+ const moved = items.filter((r) => r.type === "existing" && r.id && !r.error).length;
1289
+ const failed = items.filter((r) => !r.id || r.error).length;
1290
+ const sectionItem = items.find((r) => r.type === "section");
1291
+ if (data.dryRun) {
1292
+ console.log(`[dry run] Would arrange ${items.filter((i) => i.type === "existing").length} node(s)` +
1293
+ (opts.section ? ` into ${opts.section}` : "") +
1294
+ " (no changes made).");
1295
+ }
1296
+ else {
1297
+ console.log(`Arranged ${moved} node(s)` +
1298
+ (failed > 0 ? ` (${failed} failed)` : "") +
1299
+ (opts.section ? ` into ${data.targetSectionDisplayId || opts.section}` : ""));
1300
+ }
1301
+ if (sectionItem &&
1302
+ sectionItem.width != null &&
1303
+ sectionItem.height != null) {
1304
+ console.log(` Frame → ${Math.round(Number(sectionItem.width))} × ${Math.round(Number(sectionItem.height))}px` +
1305
+ (autoResize ? "" : " (auto-resize off)"));
1306
+ }
1307
+ else if (bbox?.width && bbox?.height) {
1308
+ console.log(`Layout bounding box: ${Math.round(bbox.width)} × ${Math.round(bbox.height)}px`);
1309
+ }
1310
+ for (const item of items) {
1311
+ if (item.error) {
1312
+ console.log(` ✗ item[${item.index}] (${item.type}): ${item.error}`);
1313
+ }
1314
+ }
1315
+ if (link)
1316
+ console.log(`Link: ${link}`);
1317
+ }
1318
+ catch (error) {
1319
+ handleError(error, cfg.json);
1320
+ }
1321
+ });
1115
1322
  canvas
1116
1323
  .command("bulk-move")
1117
1324
  .description("Move multiple nodes to new positions in one operation")
1118
1325
  .argument("<canvasId>", "Canvas ID (used for routing/auth)")
1119
- .requiredOption("--moves <json>", "JSON array of {nodeId, nodeType, x, y} objects (nodeType: note|text|list|canvas|richtext|section)")
1326
+ .requiredOption("--moves <json>", "JSON array of {nodeId, nodeType, x, y} objects (nodeType: note|text|list|canvas|richtext|section; `text` is legacy — old heading nodes, no longer creatable)")
1120
1327
  .action(async (canvasId, opts) => {
1121
1328
  const cfg = resolveConfig(program.opts());
1122
1329
  const api = new ApiClient(cfg.serverUrl, cfg.token);
@@ -1125,6 +1332,8 @@ Placement (relative to nodes already on the canvas):
1125
1332
  if (!Array.isArray(items) || items.length === 0) {
1126
1333
  throw new ValidationError("--moves must be a non-empty JSON array");
1127
1334
  }
1335
+ // `text` is a LEGACY kind (the old "Heading" node): it can no longer be created,
1336
+ // but existing ones must stay movable and deletable, so it stays valid here.
1128
1337
  const validNodeTypes = new Set(["note", "text", "list", "canvas", "richtext", "section"]);
1129
1338
  const moves = items.map((item, i) => {
1130
1339
  const positionX = Number(item.x);
@@ -1310,6 +1519,73 @@ Placement (relative to nodes already on the canvas):
1310
1519
  handleError(error, cfg.json);
1311
1520
  }
1312
1521
  });
1522
+ canvas
1523
+ .command("update-section")
1524
+ .description("Resize, rename, move, or recolor an existing section frame IN PLACE, keeping its SECTION-<n> id. Use this to grow a frame around new content — never delete + re-add, which mints a NEW id (SECTION-51 becomes SECTION-57) and breaks every [[SECTION-51]] mention pointing at it.")
1525
+ .argument("<canvasId>", "Canvas ID")
1526
+ .requiredOption("--section <id>", "Section to update — SECTION-<n> display ID or a raw node ID")
1527
+ .option("--name <name>", "New frame title (title case)")
1528
+ .option("--x <n>", "New X position (top-left)")
1529
+ .option("--y <n>", "New Y position (top-left)")
1530
+ .option("--width <n>", "New frame width in px")
1531
+ .option("--height <n>", "New frame height in px")
1532
+ .option("--color <hex>", "New frame tint hex")
1533
+ .option("--clear-color", "Reset the frame tint to the default")
1534
+ .addHelpText("after", `
1535
+ Examples:
1536
+ # Grow a frame to fit content that has spilled out of it
1537
+ $ cnotes canvas update-section CANVAS-250 --section SECTION-51 --height 1800
1538
+
1539
+ # Rename without touching the id or anything inside it
1540
+ $ cnotes canvas update-section CANVAS-250 --section SECTION-51 --name "Open Questions"
1541
+ `)
1542
+ .action(async (canvasId, opts) => {
1543
+ const cfg = resolveConfig(program.opts());
1544
+ const api = new ApiClient(cfg.serverUrl, cfg.token);
1545
+ try {
1546
+ // Reject non-numeric input here. Number("wide") is NaN, which
1547
+ // JSON.stringify writes as null, so the server would answer "expected
1548
+ // number, received null" about a flag the user typed as a word.
1549
+ const numeric = (flag, raw) => {
1550
+ const n = Number(raw);
1551
+ if (!Number.isFinite(n)) {
1552
+ throw new ValidationError(`--${flag} must be a number (got "${raw}")`);
1553
+ }
1554
+ return n;
1555
+ };
1556
+ const body = {
1557
+ action: "updateSectionNode",
1558
+ nodeId: opts.section,
1559
+ };
1560
+ if (typeof opts.name === "string")
1561
+ body.name = opts.name;
1562
+ if (opts.x !== undefined)
1563
+ body.positionX = numeric("x", opts.x);
1564
+ if (opts.y !== undefined)
1565
+ body.positionY = numeric("y", opts.y);
1566
+ if (opts.width !== undefined)
1567
+ body.width = numeric("width", opts.width);
1568
+ if (opts.height !== undefined)
1569
+ body.height = numeric("height", opts.height);
1570
+ // '' is the server's "clear the tint" sentinel.
1571
+ if (opts.clearColor)
1572
+ body.colorHex = "";
1573
+ else if (typeof opts.color === "string")
1574
+ body.colorHex = opts.color;
1575
+ // workspaceId is what lets the server resolve a CANVAS-<n> handle (the
1576
+ // form every example here uses) rather than demanding a raw Convex id.
1577
+ const data = await api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(cfg.workspaceId)}`, body);
1578
+ if (cfg.json) {
1579
+ outputJson(data);
1580
+ }
1581
+ else {
1582
+ console.log(`Updated section ${opts.section}`);
1583
+ }
1584
+ }
1585
+ catch (error) {
1586
+ handleError(error, cfg.json);
1587
+ }
1588
+ });
1313
1589
  canvas
1314
1590
  .command("move-node")
1315
1591
  .description("Move a node to a new position on a canvas")
@@ -1317,7 +1593,8 @@ Placement (relative to nodes already on the canvas):
1317
1593
  .requiredOption("--node <nodeId>", "Canvas node ID")
1318
1594
  .requiredOption("--x <n>", "X position")
1319
1595
  .requiredOption("--y <n>", "Y position")
1320
- .addOption(new Option("--type <type>", "Node type (defaults to note)").choices(["note", "text", "list", "canvas", "richtext", "section"]))
1596
+ // `text` is a LEGACY kind (the old "Heading" node) — movable/deletable, not creatable.
1597
+ .addOption(new Option("--type <type>", "Node type (defaults to note; `text` is legacy — old heading nodes, no longer creatable)").choices(["note", "text", "list", "canvas", "richtext", "section"]))
1321
1598
  .action(async (canvasId, opts) => {
1322
1599
  const cfg = resolveConfig(program.opts());
1323
1600
  const api = new ApiClient(cfg.serverUrl, cfg.token);