@valbuild/shared 0.151.0 → 0.152.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.
@@ -10,7 +10,7 @@ var wrapNativeSuper = require('../../dist/wrapNativeSuper-933f991e.cjs.dev.js');
10
10
  var zodValidationError = require('zod-validation-error');
11
11
  var fp = require('@valbuild/core/fp');
12
12
  var patch = require('@valbuild/core/patch');
13
- var getSitemapTree = require('../../dist/getSitemapTree-59c5ed5d.cjs.dev.js');
13
+ var getSitemapTree = require('../../dist/getSitemapTree-dc61b8b6.cjs.dev.js');
14
14
  var FlexSearch = require('flexsearch');
15
15
  var utils = require('../../dist/utils-d3d8b4b5.cjs.dev.js');
16
16
 
@@ -3086,6 +3086,26 @@ var Api = {
3086
3086
  parentRef: ParentRef,
3087
3087
  headVersion: zod.z.number().optional()
3088
3088
  })
3089
+ }),
3090
+ /*
3091
+ * The content service failed to take a patch: it answered with a
3092
+ * server error, or could not be reached. Not the 400 above either,
3093
+ * because the patches are fine -- the Studio keeps them and offers
3094
+ * to send them again. `message` is for the editor and `details` (the
3095
+ * status line and the service's own words) for whoever they ask.
3096
+ *
3097
+ * As with the 403, the patches BEFORE the failed one were saved.
3098
+ */
3099
+ zod.z.object({
3100
+ status: zod.z.literal(502),
3101
+ json: zod.z.object({
3102
+ type: zod.z.literal("service-error"),
3103
+ message: zod.z.string(),
3104
+ details: zod.z.string().optional(),
3105
+ newPatchIds: zod.z.array(PatchId),
3106
+ parentRef: ParentRef,
3107
+ headVersion: zod.z.number().optional()
3108
+ })
3089
3109
  }), zod.z.object({
3090
3110
  status: zod.z.literal(200),
3091
3111
  json: zod.z.object({
@@ -3121,7 +3141,9 @@ var Api = {
3121
3141
  status: zod.z.literal(500),
3122
3142
  json: zod.z.object({
3123
3143
  message: zod.z.string(),
3124
- error: GenericError
3144
+ error: GenericError,
3145
+ /** What the content service said: its status line and words. */
3146
+ details: zod.z.string().optional()
3125
3147
  })
3126
3148
  }), zod.z.object({
3127
3149
  status: zod.z.literal(200),
@@ -8239,24 +8261,30 @@ function emptyRecord(schema, context) {
8239
8261
  }));
8240
8262
  }
8241
8263
 
8242
- var toolNames = ["get_all_schema", "get_source", "search_content", "validate_content", "create_patch", "add_session_image_to_gallery", "remove_image_gallery_entry", "navigate_to", "get_current_context", "get_patches", "get_source_path_from_route", "set_session_name", "show_compare_view", "ask_user_question", "duplicate_source", "empty_at_path", "count_entries", "get_record_keys", "list_sketchpads", "create_sketchpad", "read_sketchpad", "write_sketchpad"];
8264
+ var toolNames = ["get_all_schema", "get_source", "search_content", "validate_content", "create_patch", "add_session_image_to_gallery", "remove_image_gallery_entry", "convert_session_image", "navigate_to", "get_current_context", "get_patches", "get_source_path_from_route", "set_session_name", "show_compare_view", "ask_user_question", "duplicate_source", "empty_at_path", "count_entries", "get_record_keys", "list_sketchpads", "create_sketchpad", "read_sketchpad", "write_sketchpad", "highlight_sketchpad_blocks", "clear_sketchpad_highlights"];
8243
8265
 
8244
8266
  /**
8245
8267
  * A page sketch: a page written as indented text, with four primitives.
8246
8268
  *
8247
8269
  * ```
8270
+ * --- /about a route: what follows is the page at /about
8248
8271
  * # Our people text (a heading: #, ## or ###)
8249
8272
  * We are 40 people in Oslo. text (a paragraph)
8273
+ * ## Hi {field: /x.val.ts?p="t"} text linked to a content field: typing
8274
+ * in it writes the field
8250
8275
  * [image: Team photo] placeholder: [kind] or [kind: label]
8251
8276
  * [hero: Roof](image: /a.jpg) a placeholder with an image (or video) in it
8252
8277
  * icon hero M3 5h18v14H3z how every [hero] box is drawn: SVG path data
8253
8278
  * row its children side by side
8254
- * col 2 its children stacked; 2 shares of the row
8255
- * ## Design
8279
+ * col its children stacked; columns share a row
8280
+ * ## Design equally
8256
8281
  * col
8257
8282
  * [button: Join us]
8258
8283
  * ```
8259
8284
  *
8285
+ * A sketchpad is not one page's: a route divider starts the part about a
8286
+ * page, so one sketch can cover a page and the two it links to.
8287
+ *
8260
8288
  * It is a scratch pad, not a layout language: it says WHAT is on a page and
8261
8289
  * roughly WHERE, so an assistant and a person can agree on a page's content
8262
8290
  * before anyone worries about how it looks. The person never sees this text,
@@ -8275,7 +8303,21 @@ var toolNames = ["get_all_schema", "get_source", "search_content", "validate_con
8275
8303
  * that builds the page reads it exactly as the person left it.
8276
8304
  */
8277
8305
 
8306
+ /** The start of the part about one page. Only at the top level. */
8307
+
8308
+ /** Columns share their row equally: a sketch says what, not how wide. */
8309
+
8310
+ /**
8311
+ * `src` names a file the project has, never a guess: its `path` exactly as
8312
+ * the content has it (`/public/val/hero.webp`, or its remote URL), or an
8313
+ * image uploaded to the assistant's session, `session:<key>`. Which files
8314
+ * those are is the Studio's to check; the text only holds the name.
8315
+ */
8316
+
8317
+ /** The prefix of a `src` that is an image in the assistant's session. */
8318
+ var SESSION_MEDIA_PREFIX = "session:";
8278
8319
  var CONTAINER = /^(row|col)(?:\s+(\d+))?$/;
8320
+ var ROUTE = /^---(?:\s+(.*))?$/;
8279
8321
  var PLACEHOLDER = /^\[([^\]:]*)(?::([^\]]*))?\](?:\((image|video):\s*([^\s)]+)\s*\))?$/;
8280
8322
  var HAS_MEDIA = /^\[[^\]]*\]\(/;
8281
8323
  var ICON = /^icon\s+(\S+)\s+([Mm][\d\s.-].*)$/;
@@ -8318,6 +8360,32 @@ function parseSketch(source) {
8318
8360
  while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
8319
8361
  stack.pop();
8320
8362
  }
8363
+ var route = ROUTE.exec(content);
8364
+ if (route) {
8365
+ var _route$;
8366
+ var _path = ((_route$ = route[1]) !== null && _route$ !== void 0 ? _route$ : "").trim();
8367
+ if (!_path.startsWith("/") || /\s/.test(_path)) {
8368
+ errors.push({
8369
+ line: line,
8370
+ message: 'A route divider names one page by its route, like "--- /about".'
8371
+ });
8372
+ }
8373
+ if (indent > 0) {
8374
+ errors.push({
8375
+ line: line,
8376
+ message: "A route divider starts a page, so it goes at the top level, not indented."
8377
+ });
8378
+ }
8379
+ // It closes everything open: what follows is another page.
8380
+ stack.length = 1;
8381
+ stack[0].lastIndent = 0;
8382
+ blocks.push({
8383
+ type: "route",
8384
+ line: line,
8385
+ route: _path
8386
+ });
8387
+ return;
8388
+ }
8321
8389
  var parent = stack[stack.length - 1];
8322
8390
  var node = parseContent(content, line, errors);
8323
8391
 
@@ -8359,7 +8427,27 @@ function parseSketch(source) {
8359
8427
  errors: errors
8360
8428
  };
8361
8429
  }
8362
- function parseContent(content, line, errors) {
8430
+
8431
+ /** The link to a content field at the end of a text line. */
8432
+ var FIELD = / \{field: (\/[^{}]*)\}$/;
8433
+ function parseContent(whole, line, errors) {
8434
+ var link = FIELD.exec(whole);
8435
+ if (link) {
8436
+ var node = parseLine(whole.slice(0, link.index), line, errors);
8437
+ if (node.type !== "text") {
8438
+ errors.push({
8439
+ line: line,
8440
+ message: "Only a heading or a paragraph is linked to a field: put {field: ...} at the end of one."
8441
+ });
8442
+ return node;
8443
+ }
8444
+ return createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, node), {}, {
8445
+ field: link[1].trim()
8446
+ });
8447
+ }
8448
+ return parseLine(whole, line, errors);
8449
+ }
8450
+ function parseLine(content, line, errors) {
8363
8451
  // A leading backslash keeps a line that looks like a keyword as text.
8364
8452
  if (content.startsWith("\\")) {
8365
8453
  return {
@@ -8384,17 +8472,15 @@ function parseContent(content, line, errors) {
8384
8472
  children: []
8385
8473
  };
8386
8474
  }
8387
- var weight = container[2] === undefined ? 1 : Number(container[2]);
8388
- if (weight === 0) {
8475
+ if (container[2] !== undefined) {
8389
8476
  errors.push({
8390
8477
  line: line,
8391
- message: "A col needs a weight of 1 or more."
8478
+ message: 'Columns share a row equally, so a col takes no number: write "col".'
8392
8479
  });
8393
8480
  }
8394
8481
  return {
8395
8482
  type: "col",
8396
8483
  line: line,
8397
- weight: Math.max(weight, 1),
8398
8484
  children: []
8399
8485
  };
8400
8486
  }
@@ -8468,13 +8554,19 @@ function parseContent(content, line, errors) {
8468
8554
  * The line a text block is written as. Line breaks become spaces, and a
8469
8555
  * paragraph that would read as something else is kept as text with `\`.
8470
8556
  */
8471
- function formatSketchText(level, text) {
8557
+ function formatSketchText(level, text, field) {
8472
8558
  var flat = text.replace(/\s+/g, " ").trim();
8559
+ var path = field === null || field === void 0 ? void 0 : field.replace(/\s+/g, " ").trim();
8560
+ // A source path, and one the line can be read back with: no braces.
8561
+ var link = path && /^\/[^{}]*$/.test(path) ? " {field: ".concat(path, "}") : "";
8473
8562
  if (level > 0) {
8474
- return "#".repeat(level) + " " + flat;
8563
+ return "#".repeat(level) + " " + flat + link;
8475
8564
  }
8476
- var ambiguous = flat.startsWith("\\") || flat.startsWith("[") || CONTAINER.test(flat) || HEADING.test(flat) || ICON.test(flat);
8477
- return ambiguous ? "\\" + flat : flat;
8565
+ // Read as the whole line will be, link and all: `icon x M` is text, and
8566
+ // `icon x M {field: ...}` would be an icon.
8567
+ var whole = flat + link;
8568
+ var ambiguous = flat.startsWith("\\") || flat.startsWith("[") || CONTAINER.test(whole) || ROUTE.test(whole) || HEADING.test(whole) || ICON.test(whole);
8569
+ return (ambiguous ? "\\" + flat : flat) + link;
8478
8570
  }
8479
8571
 
8480
8572
  /** The line a placeholder is written as. */
@@ -8499,17 +8591,26 @@ function formatSketchBox(placeholder) {
8499
8591
  */
8500
8592
  function formatSketch(blocks, icons) {
8501
8593
  var lines = [];
8594
+ // One block per field: the first keeps the link.
8595
+ var linked = new Set();
8502
8596
  var _walk = function walk(node, depth, inRow) {
8503
8597
  var indent = " ".repeat(depth);
8504
8598
  switch (node.type) {
8505
8599
  case "text":
8506
8600
  if (node.text) {
8507
- lines.push(indent + formatSketchText(node.level, node.text));
8601
+ var field = node.field && !linked.has(node.field) ? node.field : undefined;
8602
+ if (field) {
8603
+ linked.add(field);
8604
+ }
8605
+ lines.push(indent + formatSketchText(node.level, node.text, field));
8508
8606
  }
8509
8607
  return;
8510
8608
  case "placeholder":
8511
8609
  lines.push(indent + formatSketchBox(node));
8512
8610
  return;
8611
+ case "route":
8612
+ lines.push("--- ".concat(node.route));
8613
+ return;
8513
8614
  case "row":
8514
8615
  lines.push(indent + "row");
8515
8616
  if (node.children.length === 0) {
@@ -8526,7 +8627,7 @@ function formatSketch(blocks, icons) {
8526
8627
  });
8527
8628
  return;
8528
8629
  }
8529
- lines.push(indent + (node.weight === 1 ? "col" : "col ".concat(node.weight)));
8630
+ lines.push(indent + "col");
8530
8631
  node.children.forEach(function (child) {
8531
8632
  return _walk(child, depth + 1, false);
8532
8633
  });
@@ -8546,6 +8647,33 @@ function formatSketch(blocks, icons) {
8546
8647
  return all.length > 0 ? all.join("\n") + "\n" : "";
8547
8648
  }
8548
8649
 
8650
+ /**
8651
+ * The text of every block linked to a field, by field: what the Studio
8652
+ * writes to the content when a linked block changes. The first block wins,
8653
+ * as `formatSketch` keeps only its link.
8654
+ */
8655
+ function sketchFieldTexts(blocks) {
8656
+ var texts = new Map();
8657
+ var _walk2 = function walk(node) {
8658
+ if (node.type === "text") {
8659
+ if (node.field && node.text && !texts.has(node.field)) {
8660
+ texts.set(node.field, node.text.replace(/\s+/g, " ").trim());
8661
+ }
8662
+ } else if (node.type === "row" || node.type === "col") {
8663
+ node.children.forEach(_walk2);
8664
+ }
8665
+ };
8666
+ blocks.forEach(_walk2);
8667
+ return texts;
8668
+ }
8669
+
8670
+ /** The pages a sketch is about, in the order it gives them. */
8671
+ function sketchRoutes(blocks) {
8672
+ return blocks.flatMap(function (block) {
8673
+ return block.type === "route" && block.route !== "" ? [block.route] : [];
8674
+ });
8675
+ }
8676
+
8549
8677
  /**
8550
8678
  * What a list names a sketch by: its first top-level heading, the biggest
8551
8679
  * one first. Null for a sketch with no heading, which a list then names by
@@ -11757,7 +11885,6 @@ exports.getPageRouterSitemapTree = getSitemapTree.getPageRouterSitemapTree;
11757
11885
  exports.getPageRouterSourceFolder = getSitemapTree.getPageRouterSourceFolder;
11758
11886
  exports.getPatternFromModuleFilePath = getSitemapTree.getPatternFromModuleFilePath;
11759
11887
  exports.getTanStackRouterSourceFolder = getSitemapTree.getTanStackRouterSourceFolder;
11760
- exports.isDesignTabOrigin = getSitemapTree.isDesignTabOrigin;
11761
11888
  exports.isQuickPreviewFiles = getSitemapTree.isQuickPreviewFiles;
11762
11889
  exports.isTanStackRoutesFolder = getSitemapTree.isTanStackRoutesFolder;
11763
11890
  exports.isValCanvasFrame = getSitemapTree.isValCanvasFrame;
@@ -11766,7 +11893,6 @@ exports.isValCanvasStudioMessage = getSitemapTree.isValCanvasStudioMessage;
11766
11893
  exports.modifierLabel = getSitemapTree.modifierLabel;
11767
11894
  exports.pageLocationOf = getSitemapTree.pageLocationOf;
11768
11895
  exports.parseRoutePattern = getSitemapTree.parseRoutePattern;
11769
- exports.quickPreviewSiteOrigin = getSitemapTree.quickPreviewSiteOrigin;
11770
11896
  exports.readPageSnapshot = getSitemapTree.readPageSnapshot;
11771
11897
  exports.restoreScroll = getSitemapTree.restoreScroll;
11772
11898
  exports.screenshotPage = getSitemapTree.screenshotPage;
@@ -11829,6 +11955,7 @@ exports.RestoreVerdict = RestoreVerdict;
11829
11955
  exports.RichTextOptions = RichTextOptions;
11830
11956
  exports.SCHEMA_SOURCE_FIXES = SCHEMA_SOURCE_FIXES;
11831
11957
  exports.SELF_CHECK_VERSION = SELF_CHECK_VERSION;
11958
+ exports.SESSION_MEDIA_PREFIX = SESSION_MEDIA_PREFIX;
11832
11959
  exports.SKETCHPAD_MAX_TEXT_LENGTH = SKETCHPAD_MAX_TEXT_LENGTH;
11833
11960
  exports.STAGED_SCHEMA_PREFIX = STAGED_SCHEMA_PREFIX;
11834
11961
  exports.STUDIO_FIXES = STUDIO_FIXES;
@@ -11980,6 +12107,8 @@ exports.selfCheck = selfCheck;
11980
12107
  exports.shouldEncodeWithWasm = shouldEncodeWithWasm;
11981
12108
  exports.sideFiles = sideFiles;
11982
12109
  exports.sideSource = sideSource;
12110
+ exports.sketchFieldTexts = sketchFieldTexts;
12111
+ exports.sketchRoutes = sketchRoutes;
11983
12112
  exports.sketchTitle = sketchTitle;
11984
12113
  exports.sourcePathOfChild = sourcePathOfChild;
11985
12114
  exports.storeWork = storeWork;
@@ -10,7 +10,7 @@ var wrapNativeSuper = require('../../dist/wrapNativeSuper-8673414f.cjs.prod.js')
10
10
  var zodValidationError = require('zod-validation-error');
11
11
  var fp = require('@valbuild/core/fp');
12
12
  var patch = require('@valbuild/core/patch');
13
- var getSitemapTree = require('../../dist/getSitemapTree-6b9024d1.cjs.prod.js');
13
+ var getSitemapTree = require('../../dist/getSitemapTree-d0942fca.cjs.prod.js');
14
14
  var FlexSearch = require('flexsearch');
15
15
  var utils = require('../../dist/utils-32bcaca2.cjs.prod.js');
16
16
 
@@ -3086,6 +3086,26 @@ var Api = {
3086
3086
  parentRef: ParentRef,
3087
3087
  headVersion: zod.z.number().optional()
3088
3088
  })
3089
+ }),
3090
+ /*
3091
+ * The content service failed to take a patch: it answered with a
3092
+ * server error, or could not be reached. Not the 400 above either,
3093
+ * because the patches are fine -- the Studio keeps them and offers
3094
+ * to send them again. `message` is for the editor and `details` (the
3095
+ * status line and the service's own words) for whoever they ask.
3096
+ *
3097
+ * As with the 403, the patches BEFORE the failed one were saved.
3098
+ */
3099
+ zod.z.object({
3100
+ status: zod.z.literal(502),
3101
+ json: zod.z.object({
3102
+ type: zod.z.literal("service-error"),
3103
+ message: zod.z.string(),
3104
+ details: zod.z.string().optional(),
3105
+ newPatchIds: zod.z.array(PatchId),
3106
+ parentRef: ParentRef,
3107
+ headVersion: zod.z.number().optional()
3108
+ })
3089
3109
  }), zod.z.object({
3090
3110
  status: zod.z.literal(200),
3091
3111
  json: zod.z.object({
@@ -3121,7 +3141,9 @@ var Api = {
3121
3141
  status: zod.z.literal(500),
3122
3142
  json: zod.z.object({
3123
3143
  message: zod.z.string(),
3124
- error: GenericError
3144
+ error: GenericError,
3145
+ /** What the content service said: its status line and words. */
3146
+ details: zod.z.string().optional()
3125
3147
  })
3126
3148
  }), zod.z.object({
3127
3149
  status: zod.z.literal(200),
@@ -8239,24 +8261,30 @@ function emptyRecord(schema, context) {
8239
8261
  }));
8240
8262
  }
8241
8263
 
8242
- var toolNames = ["get_all_schema", "get_source", "search_content", "validate_content", "create_patch", "add_session_image_to_gallery", "remove_image_gallery_entry", "navigate_to", "get_current_context", "get_patches", "get_source_path_from_route", "set_session_name", "show_compare_view", "ask_user_question", "duplicate_source", "empty_at_path", "count_entries", "get_record_keys", "list_sketchpads", "create_sketchpad", "read_sketchpad", "write_sketchpad"];
8264
+ var toolNames = ["get_all_schema", "get_source", "search_content", "validate_content", "create_patch", "add_session_image_to_gallery", "remove_image_gallery_entry", "convert_session_image", "navigate_to", "get_current_context", "get_patches", "get_source_path_from_route", "set_session_name", "show_compare_view", "ask_user_question", "duplicate_source", "empty_at_path", "count_entries", "get_record_keys", "list_sketchpads", "create_sketchpad", "read_sketchpad", "write_sketchpad", "highlight_sketchpad_blocks", "clear_sketchpad_highlights"];
8243
8265
 
8244
8266
  /**
8245
8267
  * A page sketch: a page written as indented text, with four primitives.
8246
8268
  *
8247
8269
  * ```
8270
+ * --- /about a route: what follows is the page at /about
8248
8271
  * # Our people text (a heading: #, ## or ###)
8249
8272
  * We are 40 people in Oslo. text (a paragraph)
8273
+ * ## Hi {field: /x.val.ts?p="t"} text linked to a content field: typing
8274
+ * in it writes the field
8250
8275
  * [image: Team photo] placeholder: [kind] or [kind: label]
8251
8276
  * [hero: Roof](image: /a.jpg) a placeholder with an image (or video) in it
8252
8277
  * icon hero M3 5h18v14H3z how every [hero] box is drawn: SVG path data
8253
8278
  * row its children side by side
8254
- * col 2 its children stacked; 2 shares of the row
8255
- * ## Design
8279
+ * col its children stacked; columns share a row
8280
+ * ## Design equally
8256
8281
  * col
8257
8282
  * [button: Join us]
8258
8283
  * ```
8259
8284
  *
8285
+ * A sketchpad is not one page's: a route divider starts the part about a
8286
+ * page, so one sketch can cover a page and the two it links to.
8287
+ *
8260
8288
  * It is a scratch pad, not a layout language: it says WHAT is on a page and
8261
8289
  * roughly WHERE, so an assistant and a person can agree on a page's content
8262
8290
  * before anyone worries about how it looks. The person never sees this text,
@@ -8275,7 +8303,21 @@ var toolNames = ["get_all_schema", "get_source", "search_content", "validate_con
8275
8303
  * that builds the page reads it exactly as the person left it.
8276
8304
  */
8277
8305
 
8306
+ /** The start of the part about one page. Only at the top level. */
8307
+
8308
+ /** Columns share their row equally: a sketch says what, not how wide. */
8309
+
8310
+ /**
8311
+ * `src` names a file the project has, never a guess: its `path` exactly as
8312
+ * the content has it (`/public/val/hero.webp`, or its remote URL), or an
8313
+ * image uploaded to the assistant's session, `session:<key>`. Which files
8314
+ * those are is the Studio's to check; the text only holds the name.
8315
+ */
8316
+
8317
+ /** The prefix of a `src` that is an image in the assistant's session. */
8318
+ var SESSION_MEDIA_PREFIX = "session:";
8278
8319
  var CONTAINER = /^(row|col)(?:\s+(\d+))?$/;
8320
+ var ROUTE = /^---(?:\s+(.*))?$/;
8279
8321
  var PLACEHOLDER = /^\[([^\]:]*)(?::([^\]]*))?\](?:\((image|video):\s*([^\s)]+)\s*\))?$/;
8280
8322
  var HAS_MEDIA = /^\[[^\]]*\]\(/;
8281
8323
  var ICON = /^icon\s+(\S+)\s+([Mm][\d\s.-].*)$/;
@@ -8318,6 +8360,32 @@ function parseSketch(source) {
8318
8360
  while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
8319
8361
  stack.pop();
8320
8362
  }
8363
+ var route = ROUTE.exec(content);
8364
+ if (route) {
8365
+ var _route$;
8366
+ var _path = ((_route$ = route[1]) !== null && _route$ !== void 0 ? _route$ : "").trim();
8367
+ if (!_path.startsWith("/") || /\s/.test(_path)) {
8368
+ errors.push({
8369
+ line: line,
8370
+ message: 'A route divider names one page by its route, like "--- /about".'
8371
+ });
8372
+ }
8373
+ if (indent > 0) {
8374
+ errors.push({
8375
+ line: line,
8376
+ message: "A route divider starts a page, so it goes at the top level, not indented."
8377
+ });
8378
+ }
8379
+ // It closes everything open: what follows is another page.
8380
+ stack.length = 1;
8381
+ stack[0].lastIndent = 0;
8382
+ blocks.push({
8383
+ type: "route",
8384
+ line: line,
8385
+ route: _path
8386
+ });
8387
+ return;
8388
+ }
8321
8389
  var parent = stack[stack.length - 1];
8322
8390
  var node = parseContent(content, line, errors);
8323
8391
 
@@ -8359,7 +8427,27 @@ function parseSketch(source) {
8359
8427
  errors: errors
8360
8428
  };
8361
8429
  }
8362
- function parseContent(content, line, errors) {
8430
+
8431
+ /** The link to a content field at the end of a text line. */
8432
+ var FIELD = / \{field: (\/[^{}]*)\}$/;
8433
+ function parseContent(whole, line, errors) {
8434
+ var link = FIELD.exec(whole);
8435
+ if (link) {
8436
+ var node = parseLine(whole.slice(0, link.index), line, errors);
8437
+ if (node.type !== "text") {
8438
+ errors.push({
8439
+ line: line,
8440
+ message: "Only a heading or a paragraph is linked to a field: put {field: ...} at the end of one."
8441
+ });
8442
+ return node;
8443
+ }
8444
+ return createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, node), {}, {
8445
+ field: link[1].trim()
8446
+ });
8447
+ }
8448
+ return parseLine(whole, line, errors);
8449
+ }
8450
+ function parseLine(content, line, errors) {
8363
8451
  // A leading backslash keeps a line that looks like a keyword as text.
8364
8452
  if (content.startsWith("\\")) {
8365
8453
  return {
@@ -8384,17 +8472,15 @@ function parseContent(content, line, errors) {
8384
8472
  children: []
8385
8473
  };
8386
8474
  }
8387
- var weight = container[2] === undefined ? 1 : Number(container[2]);
8388
- if (weight === 0) {
8475
+ if (container[2] !== undefined) {
8389
8476
  errors.push({
8390
8477
  line: line,
8391
- message: "A col needs a weight of 1 or more."
8478
+ message: 'Columns share a row equally, so a col takes no number: write "col".'
8392
8479
  });
8393
8480
  }
8394
8481
  return {
8395
8482
  type: "col",
8396
8483
  line: line,
8397
- weight: Math.max(weight, 1),
8398
8484
  children: []
8399
8485
  };
8400
8486
  }
@@ -8468,13 +8554,19 @@ function parseContent(content, line, errors) {
8468
8554
  * The line a text block is written as. Line breaks become spaces, and a
8469
8555
  * paragraph that would read as something else is kept as text with `\`.
8470
8556
  */
8471
- function formatSketchText(level, text) {
8557
+ function formatSketchText(level, text, field) {
8472
8558
  var flat = text.replace(/\s+/g, " ").trim();
8559
+ var path = field === null || field === void 0 ? void 0 : field.replace(/\s+/g, " ").trim();
8560
+ // A source path, and one the line can be read back with: no braces.
8561
+ var link = path && /^\/[^{}]*$/.test(path) ? " {field: ".concat(path, "}") : "";
8473
8562
  if (level > 0) {
8474
- return "#".repeat(level) + " " + flat;
8563
+ return "#".repeat(level) + " " + flat + link;
8475
8564
  }
8476
- var ambiguous = flat.startsWith("\\") || flat.startsWith("[") || CONTAINER.test(flat) || HEADING.test(flat) || ICON.test(flat);
8477
- return ambiguous ? "\\" + flat : flat;
8565
+ // Read as the whole line will be, link and all: `icon x M` is text, and
8566
+ // `icon x M {field: ...}` would be an icon.
8567
+ var whole = flat + link;
8568
+ var ambiguous = flat.startsWith("\\") || flat.startsWith("[") || CONTAINER.test(whole) || ROUTE.test(whole) || HEADING.test(whole) || ICON.test(whole);
8569
+ return (ambiguous ? "\\" + flat : flat) + link;
8478
8570
  }
8479
8571
 
8480
8572
  /** The line a placeholder is written as. */
@@ -8499,17 +8591,26 @@ function formatSketchBox(placeholder) {
8499
8591
  */
8500
8592
  function formatSketch(blocks, icons) {
8501
8593
  var lines = [];
8594
+ // One block per field: the first keeps the link.
8595
+ var linked = new Set();
8502
8596
  var _walk = function walk(node, depth, inRow) {
8503
8597
  var indent = " ".repeat(depth);
8504
8598
  switch (node.type) {
8505
8599
  case "text":
8506
8600
  if (node.text) {
8507
- lines.push(indent + formatSketchText(node.level, node.text));
8601
+ var field = node.field && !linked.has(node.field) ? node.field : undefined;
8602
+ if (field) {
8603
+ linked.add(field);
8604
+ }
8605
+ lines.push(indent + formatSketchText(node.level, node.text, field));
8508
8606
  }
8509
8607
  return;
8510
8608
  case "placeholder":
8511
8609
  lines.push(indent + formatSketchBox(node));
8512
8610
  return;
8611
+ case "route":
8612
+ lines.push("--- ".concat(node.route));
8613
+ return;
8513
8614
  case "row":
8514
8615
  lines.push(indent + "row");
8515
8616
  if (node.children.length === 0) {
@@ -8526,7 +8627,7 @@ function formatSketch(blocks, icons) {
8526
8627
  });
8527
8628
  return;
8528
8629
  }
8529
- lines.push(indent + (node.weight === 1 ? "col" : "col ".concat(node.weight)));
8630
+ lines.push(indent + "col");
8530
8631
  node.children.forEach(function (child) {
8531
8632
  return _walk(child, depth + 1, false);
8532
8633
  });
@@ -8546,6 +8647,33 @@ function formatSketch(blocks, icons) {
8546
8647
  return all.length > 0 ? all.join("\n") + "\n" : "";
8547
8648
  }
8548
8649
 
8650
+ /**
8651
+ * The text of every block linked to a field, by field: what the Studio
8652
+ * writes to the content when a linked block changes. The first block wins,
8653
+ * as `formatSketch` keeps only its link.
8654
+ */
8655
+ function sketchFieldTexts(blocks) {
8656
+ var texts = new Map();
8657
+ var _walk2 = function walk(node) {
8658
+ if (node.type === "text") {
8659
+ if (node.field && node.text && !texts.has(node.field)) {
8660
+ texts.set(node.field, node.text.replace(/\s+/g, " ").trim());
8661
+ }
8662
+ } else if (node.type === "row" || node.type === "col") {
8663
+ node.children.forEach(_walk2);
8664
+ }
8665
+ };
8666
+ blocks.forEach(_walk2);
8667
+ return texts;
8668
+ }
8669
+
8670
+ /** The pages a sketch is about, in the order it gives them. */
8671
+ function sketchRoutes(blocks) {
8672
+ return blocks.flatMap(function (block) {
8673
+ return block.type === "route" && block.route !== "" ? [block.route] : [];
8674
+ });
8675
+ }
8676
+
8549
8677
  /**
8550
8678
  * What a list names a sketch by: its first top-level heading, the biggest
8551
8679
  * one first. Null for a sketch with no heading, which a list then names by
@@ -11757,7 +11885,6 @@ exports.getPageRouterSitemapTree = getSitemapTree.getPageRouterSitemapTree;
11757
11885
  exports.getPageRouterSourceFolder = getSitemapTree.getPageRouterSourceFolder;
11758
11886
  exports.getPatternFromModuleFilePath = getSitemapTree.getPatternFromModuleFilePath;
11759
11887
  exports.getTanStackRouterSourceFolder = getSitemapTree.getTanStackRouterSourceFolder;
11760
- exports.isDesignTabOrigin = getSitemapTree.isDesignTabOrigin;
11761
11888
  exports.isQuickPreviewFiles = getSitemapTree.isQuickPreviewFiles;
11762
11889
  exports.isTanStackRoutesFolder = getSitemapTree.isTanStackRoutesFolder;
11763
11890
  exports.isValCanvasFrame = getSitemapTree.isValCanvasFrame;
@@ -11766,7 +11893,6 @@ exports.isValCanvasStudioMessage = getSitemapTree.isValCanvasStudioMessage;
11766
11893
  exports.modifierLabel = getSitemapTree.modifierLabel;
11767
11894
  exports.pageLocationOf = getSitemapTree.pageLocationOf;
11768
11895
  exports.parseRoutePattern = getSitemapTree.parseRoutePattern;
11769
- exports.quickPreviewSiteOrigin = getSitemapTree.quickPreviewSiteOrigin;
11770
11896
  exports.readPageSnapshot = getSitemapTree.readPageSnapshot;
11771
11897
  exports.restoreScroll = getSitemapTree.restoreScroll;
11772
11898
  exports.screenshotPage = getSitemapTree.screenshotPage;
@@ -11829,6 +11955,7 @@ exports.RestoreVerdict = RestoreVerdict;
11829
11955
  exports.RichTextOptions = RichTextOptions;
11830
11956
  exports.SCHEMA_SOURCE_FIXES = SCHEMA_SOURCE_FIXES;
11831
11957
  exports.SELF_CHECK_VERSION = SELF_CHECK_VERSION;
11958
+ exports.SESSION_MEDIA_PREFIX = SESSION_MEDIA_PREFIX;
11832
11959
  exports.SKETCHPAD_MAX_TEXT_LENGTH = SKETCHPAD_MAX_TEXT_LENGTH;
11833
11960
  exports.STAGED_SCHEMA_PREFIX = STAGED_SCHEMA_PREFIX;
11834
11961
  exports.STUDIO_FIXES = STUDIO_FIXES;
@@ -11980,6 +12107,8 @@ exports.selfCheck = selfCheck;
11980
12107
  exports.shouldEncodeWithWasm = shouldEncodeWithWasm;
11981
12108
  exports.sideFiles = sideFiles;
11982
12109
  exports.sideSource = sideSource;
12110
+ exports.sketchFieldTexts = sketchFieldTexts;
12111
+ exports.sketchRoutes = sketchRoutes;
11983
12112
  exports.sketchTitle = sketchTitle;
11984
12113
  exports.sourcePathOfChild = sourcePathOfChild;
11985
12114
  exports.storeWork = storeWork;