@fuaran-ui/ops 0.5.0 → 0.7.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/dist/index.cjs CHANGED
@@ -385,6 +385,9 @@ var binding = (b, staticEnc = objValue) => {
385
385
  }
386
386
  case "Computed":
387
387
  return caseObj("Computed", [["fn", CLOSURE]]);
388
+ // No wire fields: the instant is host-furnished at resolve time.
389
+ case "Now":
390
+ return caseObj("Now", []);
388
391
  case "I18n": {
389
392
  const fields = [];
390
393
  if (b.args !== void 0) {
@@ -427,7 +430,10 @@ var binding = (b, staticEnc = objValue) => {
427
430
  return caseObj("Transform", [
428
431
  ...paramFields,
429
432
  ["pipeline", jArray(b.pipeline.map(transformStep))],
430
- ["source", dataSource(b.source)]
433
+ [
434
+ "source",
435
+ b.source.kind === "Data" ? dataSource(b.source.source) : binding(b.source.binding, jsonValue)
436
+ ]
431
437
  ]);
432
438
  }
433
439
  case "Invoke":
@@ -461,11 +467,12 @@ var action = (a) => {
461
467
  ]);
462
468
  case "Navigate":
463
469
  return caseObj("Navigate", [["route", str(a.route)]]);
464
- case "SetState":
465
- return caseObj("SetState", [
466
- ["key", str(a.key)],
467
- ["value", jsonValue(a.value)]
468
- ]);
470
+ case "SetState": {
471
+ const fields = [["key", str(a.key)]];
472
+ if (a.value !== void 0) fields.push(["value", jsonValue(a.value)]);
473
+ if (a.valueFrom !== void 0) fields.push(["valueFrom", binding(a.valueFrom, jsonValue)]);
474
+ return caseObj("SetState", fields);
475
+ }
469
476
  case "AiTool":
470
477
  return caseObj("AiTool", [
471
478
  ["args", jsonValue(a.args)],
@@ -521,6 +528,13 @@ var cellFormat = (f) => {
521
528
  return caseObj("SignificantDigits", [["digits", num(f.digits)]]);
522
529
  case "Date":
523
530
  return caseObj("Date", [["format", str(f.format)]]);
531
+ case "Duration":
532
+ return caseObj("Duration", [
533
+ ["style", str(f.style)],
534
+ ["unit", str(f.unit)]
535
+ ]);
536
+ case "RelativeTime":
537
+ return caseObj("RelativeTime", [["unit", str(f.unit)]]);
524
538
  case "Custom":
525
539
  return caseObj("Custom", [["fn", CLOSURE]]);
526
540
  default:
@@ -539,6 +553,11 @@ var formatIntent = (f) => {
539
553
  return caseObj("Date", [["dateStyle", str(f.dateStyle)]]);
540
554
  case "RelativeTime":
541
555
  return caseObj("RelativeTime", [["unit", str(f.unit)]]);
556
+ case "Duration":
557
+ return caseObj("Duration", [
558
+ ["style", str(f.style)],
559
+ ["unit", str(f.unit)]
560
+ ]);
542
561
  default:
543
562
  return assertNever(f);
544
563
  }
@@ -634,6 +653,7 @@ var linkSpec = (s) => {
634
653
  ];
635
654
  if (s.rel !== void 0) fields.push(["rel", str(s.rel)]);
636
655
  if (s.target !== void 0) fields.push(["target", str(s.target)]);
656
+ if (s.protection !== void 0) fields.push(["protection", str(s.protection)]);
637
657
  return jObject(fields);
638
658
  };
639
659
  var imageSpec = (s) => jObject([
@@ -671,6 +691,13 @@ var mathSpec = (s) => jObject([
671
691
  ]);
672
692
  var sparklineSpec = (s) => jObject([["source", binding(s.source, staticFloatSeq)]]);
673
693
  var skeletonSpec = (s) => jObject([["rows", num(s.rows)]]);
694
+ var iconSpec = (s) => {
695
+ const fields = [["icon", str(s.icon)]];
696
+ if (s.label !== void 0) fields.push(["label", str(s.label)]);
697
+ if (s.size !== "Medium") fields.push(["size", str(s.size)]);
698
+ pushToneOptional(fields, s.tone);
699
+ return jObject(fields);
700
+ };
674
701
  var calloutSpec = (s) => {
675
702
  const fields = [
676
703
  ["body", textSource(s.body)],
@@ -714,6 +741,8 @@ var drawStyle = (s) => {
714
741
  if (s.emphasis !== void 0) fields.push(["emphasis", str(s.emphasis)]);
715
742
  if (s.fontFamily !== void 0) fields.push(["fontFamily", str(s.fontFamily)]);
716
743
  if (s.markId !== void 0) fields.push(["markId", str(s.markId)]);
744
+ if (s.rotation !== void 0) fields.push(["rotation", num(s.rotation)]);
745
+ if (s.tip !== void 0) fields.push(["tip", textSource(s.tip)]);
717
746
  return jObject(fields);
718
747
  };
719
748
  var curveCommand = (c) => {
@@ -830,6 +859,8 @@ var displayKind = (d) => {
830
859
  return hoistSpec("Progress", progressSpec(d.spec));
831
860
  case "Skeleton":
832
861
  return hoistSpec("Skeleton", skeletonSpec(d.spec));
862
+ case "Icon":
863
+ return hoistSpec("Icon", iconSpec(d.spec));
833
864
  case "LabelValueRow":
834
865
  return hoistSpec("LabelValueRow", labelValueRowSpec(d.spec));
835
866
  case "Fact":
@@ -879,6 +910,11 @@ var formFieldKind = (autoBind, k) => {
879
910
  ...handlerField("onToggle", k.onToggle),
880
911
  ...valueField(k.value, schema.controlValueDefaults.checkbox, (v) => binding(v))
881
912
  ]);
913
+ case "Toggle":
914
+ return caseObj("Toggle", [
915
+ ...handlerField("onToggle", k.onToggle),
916
+ ...valueField(k.value, schema.controlValueDefaults.checkbox, (v) => binding(v))
917
+ ]);
882
918
  case "Choice":
883
919
  return caseObj("Choice", [
884
920
  ...handlerField("onChange", k.onChange),
@@ -1132,8 +1168,14 @@ var columnErased = (c) => {
1132
1168
  pushColumnWidthOptional(fields, "width", c.width);
1133
1169
  if (c.value !== void 0) fields.push(["value", CLOSURE]);
1134
1170
  if (c.field !== void 0) fields.push(["field", str(c.field)]);
1171
+ if (c.sortable !== void 0) fields.push(["sortable", bool(c.sortable)]);
1172
+ if (c.editable !== void 0) fields.push(["editable", bool(c.editable)]);
1135
1173
  return jObject(fields);
1136
1174
  };
1175
+ var defaultSortJson = (d) => jObject([
1176
+ ["column", intLit(d.column)],
1177
+ ["direction", str(d.direction)]
1178
+ ]);
1137
1179
  var gridSpec = (s) => {
1138
1180
  const fields = [
1139
1181
  ["columns", jArray(s.columns.map(columnErased))],
@@ -1145,14 +1187,22 @@ var gridSpec = (s) => {
1145
1187
  if (s.onRowClick !== void 0) fields.push(["onRowClick", CLOSURE]);
1146
1188
  if (s.rowKey !== void 0) fields.push(["rowKey", CLOSURE]);
1147
1189
  if (s.rowKeyField !== void 0) fields.push(["rowKeyField", str(s.rowKeyField)]);
1148
- if (s.staticRows !== void 0)
1149
- fields.push([
1150
- "staticRows",
1151
- jObject([
1152
- ["headers", jArray(s.staticRows.headers.map(textSource))],
1153
- ["rows", jArray(s.staticRows.rows.map((row) => jArray(row.map(textSource))))]
1154
- ])
1155
- ]);
1190
+ if (s.sortStateKey !== void 0) fields.push(["sortStateKey", str(s.sortStateKey)]);
1191
+ if (s.pageSize !== void 0) fields.push(["pageSize", num(s.pageSize)]);
1192
+ if (s.pageStateKey !== void 0) fields.push(["pageStateKey", str(s.pageStateKey)]);
1193
+ if (s.defaultSort !== void 0) fields.push(["defaultSort", defaultSortJson(s.defaultSort)]);
1194
+ if (s.editStateKey !== void 0) fields.push(["editStateKey", str(s.editStateKey)]);
1195
+ if (s.staticRows !== void 0) {
1196
+ const sr = s.staticRows;
1197
+ const srFields = [
1198
+ ["headers", jArray(sr.headers.map(textSource))],
1199
+ ["rows", jArray(sr.rows.map((row) => jArray(row.map(textSource))))]
1200
+ ];
1201
+ if (sr.sortable !== void 0) srFields.push(["sortable", bool(sr.sortable)]);
1202
+ if (sr.defaultSort !== void 0)
1203
+ srFields.push(["defaultSort", defaultSortJson(sr.defaultSort)]);
1204
+ fields.push(["staticRows", jObject(srFields)]);
1205
+ }
1156
1206
  return jObject(fields);
1157
1207
  };
1158
1208
  var chartSpec = (s) => {
@@ -1165,6 +1215,13 @@ var chartSpec = (s) => {
1165
1215
  ["yFields", jArray(s.yFields.map(str))]
1166
1216
  ];
1167
1217
  if (s.title !== void 0) fields.push(["title", textSource(s.title)]);
1218
+ if (s.valueFormat !== void 0) fields.push(["valueFormat", formatIntent(s.valueFormat)]);
1219
+ if (s.xTitle !== void 0) fields.push(["xTitle", textSource(s.xTitle)]);
1220
+ if (s.yTitle !== void 0) fields.push(["yTitle", textSource(s.yTitle)]);
1221
+ if (s.subtitle !== void 0) fields.push(["subtitle", textSource(s.subtitle)]);
1222
+ if (s.legendPosition !== void 0) fields.push(["legendPosition", str(s.legendPosition)]);
1223
+ if (s.dataLabels !== void 0) fields.push(["dataLabels", str(s.dataLabels)]);
1224
+ if (s.xScale !== void 0) fields.push(["xScale", str(s.xScale)]);
1168
1225
  if (s.onPointClick !== void 0) fields.push(["onPointClick", CLOSURE]);
1169
1226
  return jObject(fields);
1170
1227
  };
@@ -1444,7 +1501,10 @@ var nodeKind = (k) => {
1444
1501
  )
1445
1502
  ],
1446
1503
  ["default", node(k.spec.default)],
1447
- ["stateKey", str(k.spec.stateKey)]
1504
+ // Phase 768 collapse rule — State(key, no default) keeps the compact
1505
+ // canonical `stateKey` spelling (existing fixtures stay byte-identical);
1506
+ // any other selector encodes as `on`.
1507
+ k.spec.on.kind === "State" && k.spec.on.defaultValue === void 0 ? ["stateKey", str(k.spec.on.key)] : ["on", binding(k.spec.on, str)]
1448
1508
  ]);
1449
1509
  case "FragmentDecl": {
1450
1510
  const fields = [
@@ -3080,12 +3140,23 @@ var decodeTextAnchor = (p, j) => bareEnum(p, j, ["Start", "Middle", "End"], "Tex
3080
3140
  var decodeStyleRole = (p, j) => bareEnum(p, j, ["None", "Eyebrow", "Data", "Lede", "Caption"], "StyleRole");
3081
3141
  var decodeFontVoice = (p, j) => bareEnum(p, j, ["Default", "Display", "Structural"], "FontVoice");
3082
3142
  var decodeChartKind = (p, j) => bareEnum(p, j, ["Line", "Bar", "Area", "Pie", "Scatter", "Heatmap"], "ChartKind");
3143
+ var decodeChartLegendPosition = (p, j) => bareEnum(p, j, ["Top", "Right", "Bottom", "None"], "ChartLegendPosition");
3144
+ var decodeChartDataLabels = (p, j) => bareEnum(p, j, ["Off", "Ends"], "ChartDataLabels");
3145
+ var decodeChartXScale = (p, j) => bareEnum(p, j, ["Category", "Temporal"], "ChartXScale");
3083
3146
  var decodeFileReadEncoding = (p, j) => bareEnum(p, j, ["Text", "Base64", "DataUrl"], "FileReadEncoding");
3084
3147
  var decodeAriaRole = (p, j) => {
3085
3148
  if (j.kind !== "JString") return wrongType(p, "JSON string (ARIA role)");
3086
3149
  return ok(j.value);
3087
3150
  };
3088
3151
  var decodeLiveRegion = (p, j) => bareEnum(p, j, ["polite", "assertive", "off"], "LiveRegionKind");
3152
+ var decodeDurationUnit = (p, j) => bareEnum(p, j, ["Seconds", "Minutes", "Hours"], "DurationUnit");
3153
+ var decodeDurationStyle = (p, j) => bareEnum(p, j, ["Compact", "Clock", "Long"], "DurationStyle");
3154
+ var decodeRelativeTimeUnit = (p, j) => bareEnum(
3155
+ p,
3156
+ j,
3157
+ ["Second", "Minute", "Hour", "Day", "Week", "Month", "Year"],
3158
+ "RelativeTimeUnit"
3159
+ );
3089
3160
  var decodeCellFormat = (path, j) => {
3090
3161
  const fo = requireObject(path, j);
3091
3162
  if (!fo.ok) return fo;
@@ -3120,13 +3191,24 @@ var decodeCellFormat = (path, j) => {
3120
3191
  const r = reqField(path, f, "format", "format string", requireString);
3121
3192
  return r.ok ? ok({ kind: "Date", format: r.value }) : r;
3122
3193
  }
3194
+ case "Duration": {
3195
+ const unit = reqField(path, f, "unit", "DurationUnit string", decodeDurationUnit);
3196
+ if (!unit.ok) return unit;
3197
+ const style = reqField(path, f, "style", "DurationStyle string", decodeDurationStyle);
3198
+ if (!style.ok) return style;
3199
+ return ok({ kind: "Duration", unit: unit.value, style: style.value });
3200
+ }
3201
+ case "RelativeTime": {
3202
+ const r = reqField(path, f, "unit", "RelativeTimeUnit string", decodeRelativeTimeUnit);
3203
+ return r.ok ? ok({ kind: "RelativeTime", unit: r.value }) : r;
3204
+ }
3123
3205
  case "Custom":
3124
3206
  return ok({ kind: "Custom", format: () => CLOSURE2 });
3125
3207
  default:
3126
3208
  return unknownDuCase(
3127
3209
  path,
3128
3210
  d.value,
3129
- "None | Number | Currency | Percent | SignificantDigits | Date | Custom"
3211
+ "None | Number | Currency | Percent | SignificantDigits | Date | Duration | RelativeTime | Custom"
3130
3212
  );
3131
3213
  }
3132
3214
  };
@@ -3164,22 +3246,22 @@ var decodeFormat = (path, j) => {
3164
3246
  return r.ok ? ok({ kind: "Date", dateStyle: r.value }) : r;
3165
3247
  }
3166
3248
  case "RelativeTime": {
3167
- const r = reqField(
3168
- path,
3169
- f,
3170
- "unit",
3171
- "RelativeTimeUnit string",
3172
- (p, v) => bareEnum(
3173
- p,
3174
- v,
3175
- ["Second", "Minute", "Hour", "Day", "Week", "Month", "Year"],
3176
- "RelativeTimeUnit"
3177
- )
3178
- );
3249
+ const r = reqField(path, f, "unit", "RelativeTimeUnit string", decodeRelativeTimeUnit);
3179
3250
  return r.ok ? ok({ kind: "RelativeTime", unit: r.value }) : r;
3180
3251
  }
3252
+ case "Duration": {
3253
+ const unit = reqField(path, f, "unit", "DurationUnit string", decodeDurationUnit);
3254
+ if (!unit.ok) return unit;
3255
+ const style = reqField(path, f, "style", "DurationStyle string", decodeDurationStyle);
3256
+ if (!style.ok) return style;
3257
+ return ok({ kind: "Duration", unit: unit.value, style: style.value });
3258
+ }
3181
3259
  default:
3182
- return unknownDuCase(path, d.value, "Number | Currency | Percent | Date | RelativeTime");
3260
+ return unknownDuCase(
3261
+ path,
3262
+ d.value,
3263
+ "Number | Currency | Percent | Date | RelativeTime | Duration"
3264
+ );
3183
3265
  }
3184
3266
  };
3185
3267
  var decodeLocaleSource = (path, j) => {
@@ -4166,6 +4248,13 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4166
4248
  }
4167
4249
  case "Computed":
4168
4250
  return ok({ kind: "Computed", compute: () => void 0 });
4251
+ // The projection decodes to the IDENTITY (the Phase 427 Selection fix
4252
+ // replayed): the host-furnished instant is already the wire-shaped string,
4253
+ // so a decoded reader receives it as-is. A value-discarding placeholder
4254
+ // here would make every decoded `Now` resolve to nothing even when the
4255
+ // host furnishes the instant.
4256
+ case "Now":
4257
+ return ok({ kind: "Now", project: (iso) => iso });
4169
4258
  case "I18n": {
4170
4259
  const key = reqField(path, f, "key", "i18n key string", requireString);
4171
4260
  if (!key.ok) return key;
@@ -4226,8 +4315,35 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4226
4315
  if (!srcJ.ok) return srcJ;
4227
4316
  const pipeJ = requireField(path, f, "pipeline", "Transform pipeline array");
4228
4317
  if (!pipeJ.ok) return pipeJ;
4229
- const src = decodeDataSource(srcJ.value);
4230
- if (!src.ok) return makeError("WRONG_TYPE", `${path}.source`, src.error);
4318
+ const liveTagAst = srcJ.value.kind === "JObject" ? srcJ.value.fields.get("$type") : void 0;
4319
+ const liveTag = liveTagAst !== void 0 && liveTagAst.kind === "JString" && (liveTagAst.value === "State" || liveTagAst.value === "Selection" || liveTagAst.value === "Query") ? liveTagAst.value : void 0;
4320
+ const hasCarried = srcJ.value.kind === "JObject" && srcJ.value.fields.has("defaultValue");
4321
+ let source;
4322
+ if (liveTag === void 0 || liveTag === "State" && !hasCarried) {
4323
+ const src = decodeDataSource(normaliseTransformSource(srcJ.value));
4324
+ if (!src.ok) return makeError("WRONG_TYPE", `${path}.source`, src.error);
4325
+ source = { kind: "Data", source: src.value };
4326
+ } else {
4327
+ const b2 = decodeBinding(`${path}.source`, srcJ.value, decodeJVal, void 0);
4328
+ if (!b2.ok) return b2;
4329
+ if (liveTag === "State") {
4330
+ const snap = decodeDataSource(normaliseTransformSource(srcJ.value));
4331
+ if (!snap.ok) return makeError("WRONG_TYPE", `${path}.source`, snap.error);
4332
+ source = {
4333
+ kind: "Live",
4334
+ binding: b2.value,
4335
+ initial: snap.value
4336
+ };
4337
+ } else {
4338
+ const dv = srcJ.value.kind === "JObject" ? srcJ.value.fields.get("defaultValue") : void 0;
4339
+ const snap = dv !== void 0 ? decodeDataSource(normaliseTransformSource(dv)) : void 0;
4340
+ source = {
4341
+ kind: "Live",
4342
+ binding: b2.value,
4343
+ initial: snap !== void 0 && snap.ok ? snap.value : { kind: "Embedded", table: { schema: [], columns: [] } }
4344
+ };
4345
+ }
4346
+ }
4231
4347
  const pipe = decodePipelineCore(pipeJ.value);
4232
4348
  if (!pipe.ok) return makeError("WRONG_TYPE", `${path}.pipeline`, pipe.error);
4233
4349
  const paramsField = tryField(f, "params");
@@ -4278,7 +4394,7 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4278
4394
  }
4279
4395
  const b = {
4280
4396
  kind: "Transform",
4281
- source: src.value,
4397
+ source,
4282
4398
  pipeline: pipe.value,
4283
4399
  ...params !== void 0 ? { params } : {}
4284
4400
  };
@@ -4313,6 +4429,62 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4313
4429
  );
4314
4430
  }
4315
4431
  };
4432
+ var normaliseTransformSource = (j) => {
4433
+ let unwrapped = j;
4434
+ if (j.kind === "JObject") {
4435
+ const t = j.fields.get("$type");
4436
+ if (t !== void 0 && t.kind === "JString" && (t.value === "State" || t.value === "Static" || t.value === "Bound")) {
4437
+ const inner = j.fields.get("defaultValue") ?? j.fields.get("value");
4438
+ if (inner !== void 0) unwrapped = inner;
4439
+ }
4440
+ }
4441
+ if (unwrapped.kind === "JArray" && unwrapped.items.length > 0 && unwrapped.items[0].kind === "JObject") {
4442
+ const rows = unwrapped.items;
4443
+ const first = unwrapped.items[0];
4444
+ const keys = [...first.fields.keys()].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
4445
+ const cols = /* @__PURE__ */ new Map();
4446
+ for (const k of keys) {
4447
+ const cells = rows.map((row) => {
4448
+ if (row.kind !== "JObject") return { kind: "JNull" };
4449
+ return row.fields.get(k) ?? { kind: "JNull" };
4450
+ });
4451
+ cols.set(k, { kind: "JArray", items: cells });
4452
+ }
4453
+ return {
4454
+ kind: "JObject",
4455
+ fields: /* @__PURE__ */ new Map([["columns", { kind: "JObject", fields: cols }]])
4456
+ };
4457
+ }
4458
+ return unwrapped;
4459
+ };
4460
+ var liveValueToTable = (v) => {
4461
+ if (v === void 0) return { ok: false, error: "Transform live source resolved to no value" };
4462
+ let text;
4463
+ try {
4464
+ text = JSON.stringify(v);
4465
+ } catch {
4466
+ return {
4467
+ ok: false,
4468
+ error: "Transform live source resolved to a value with no JSON representation"
4469
+ };
4470
+ }
4471
+ if (text === void 0) {
4472
+ return { ok: false, error: "Transform live source resolved to no value" };
4473
+ }
4474
+ const parsed = parse(text);
4475
+ if (!parsed.ok) {
4476
+ return { ok: false, error: "Transform live source resolved to unreadable data" };
4477
+ }
4478
+ const src = decodeDataSource(normaliseTransformSource(parsed.value));
4479
+ if (!src.ok) return { ok: false, error: src.error };
4480
+ if (src.value.kind !== "Embedded") {
4481
+ return {
4482
+ ok: false,
4483
+ error: `Transform live source resolved to a 'ref' source ('${src.value.name}'), which is not host-resolved`
4484
+ };
4485
+ }
4486
+ return { ok: true, value: src.value.table };
4487
+ };
4316
4488
  var decodeBindingArgs = (path, j) => {
4317
4489
  const fo = requireObject(path, j);
4318
4490
  if (!fo.ok) return fo;
@@ -4577,9 +4749,32 @@ var decodeAction = (path, j) => {
4577
4749
  case "SetState": {
4578
4750
  const key = reqField(path, f, "key", "state key string", requireString);
4579
4751
  if (!key.ok) return key;
4580
- const valueJ = requireField(path, f, "value", "JsonValue value");
4581
- if (!valueJ.ok) return valueJ;
4582
- const value = decodeJVal(`${path}.value`, valueJ.value);
4752
+ const valueJ = tryField(f, "value");
4753
+ const fromJ = tryField(f, "valueFrom");
4754
+ if (valueJ !== void 0 && fromJ !== void 0) {
4755
+ return makeError(
4756
+ "WRONG_TYPE",
4757
+ `${path}.valueFrom`,
4758
+ "SetState carries both 'value' and 'valueFrom' \u2014 exactly one is allowed: 'value' is a literal JSON value written verbatim; 'valueFrom' derives the written value from a Binding at dispatch time; remove one"
4759
+ );
4760
+ }
4761
+ if (valueJ === void 0 && fromJ === void 0) {
4762
+ return makeError(
4763
+ "MISSING_FIELD",
4764
+ `${path}.value`,
4765
+ "missing required field 'value' \u2014 provide 'value' (a literal JSON value) or 'valueFrom' (a Binding evaluated at dispatch time)"
4766
+ );
4767
+ }
4768
+ if (fromJ !== void 0) {
4769
+ const from = decodeBinding(`${path}.valueFrom`, fromJ, decodeJVal, void 0);
4770
+ if (!from.ok) return from;
4771
+ return ok({
4772
+ kind: "SetState",
4773
+ key: key.value,
4774
+ valueFrom: from.value
4775
+ });
4776
+ }
4777
+ const value = decodeJVal(`${path}.value`, valueJ);
4583
4778
  return value.ok ? ok({ kind: "SetState", key: key.value, value: value.value }) : value;
4584
4779
  }
4585
4780
  case "AiTool": {
@@ -4788,14 +4983,18 @@ var decodeLinkSpec = (path, j) => {
4788
4983
  if (!rel.ok) return rel;
4789
4984
  const target = optField(path, f, "target", requireString);
4790
4985
  if (!target.ok) return target;
4986
+ const protection = optField(path, f, "protection", decodeLinkProtection);
4987
+ if (!protection.ok) return protection;
4791
4988
  return ok({
4792
4989
  href: href.value,
4793
4990
  label: label.value,
4794
4991
  download: download.value,
4795
4992
  ...rel.value !== void 0 ? { rel: rel.value } : {},
4796
- ...target.value !== void 0 ? { target: target.value } : {}
4993
+ ...target.value !== void 0 ? { target: target.value } : {},
4994
+ ...protection.value !== void 0 ? { protection: protection.value } : {}
4797
4995
  });
4798
4996
  };
4997
+ var decodeLinkProtection = (p, j) => bareEnum(p, j, ["email"], "LinkProtection");
4799
4998
  var decodeImageSpec = (path, j) => {
4800
4999
  const fo = requireObject(path, j);
4801
5000
  if (!fo.ok) return fo;
@@ -4902,6 +5101,28 @@ var decodeSkeletonSpec = (path, j) => {
4902
5101
  const rows = reqField(path, fo.value, "rows", "skeleton row count integer", requireInt);
4903
5102
  return rows.ok ? ok({ rows: rows.value }) : rows;
4904
5103
  };
5104
+ var decodeIconSize = (p, j) => bareEnum(p, j, ["Small", "Medium", "Large"], "IconSize");
5105
+ var decodeIconSpec = (path, j) => {
5106
+ const fo = requireObject(path, j);
5107
+ if (!fo.ok) return fo;
5108
+ const f = fo.value;
5109
+ const icon = reqField(path, f, "icon", "icon name string", requireString);
5110
+ if (!icon.ok) return icon;
5111
+ const sizeJ = tryField(f, "size");
5112
+ const size = sizeJ === void 0 ? ok("Medium") : decodeIconSize(`${path}.size`, sizeJ);
5113
+ if (!size.ok) return size;
5114
+ const toneJ = tryField(f, "tone");
5115
+ const tone = toneJ === void 0 ? ok("Default") : decodeTone(`${path}.tone`, toneJ);
5116
+ if (!tone.ok) return tone;
5117
+ const label = optField(path, f, "label", requireString);
5118
+ if (!label.ok) return label;
5119
+ return ok({
5120
+ icon: icon.value,
5121
+ size: size.value,
5122
+ tone: tone.value,
5123
+ ...label.value !== void 0 ? { label: label.value } : {}
5124
+ });
5125
+ };
4905
5126
  var decodeCalloutSpec = (path, j) => {
4906
5127
  const fo = requireObject(path, j);
4907
5128
  if (!fo.ok) return fo;
@@ -4992,6 +5213,10 @@ var decodeDrawStyle = (path, j) => {
4992
5213
  if (!fontFamily.ok) return fontFamily;
4993
5214
  const markId = optField(path, f, "markId", requireString);
4994
5215
  if (!markId.ok) return markId;
5216
+ const rotation = optField(path, f, "rotation", requireFloat);
5217
+ if (!rotation.ok) return rotation;
5218
+ const tip = optField(path, f, "tip", decodeTextSource);
5219
+ if (!tip.ok) return tip;
4995
5220
  return ok({
4996
5221
  ...fill.value !== void 0 ? { fill: fill.value } : {},
4997
5222
  ...stroke.value !== void 0 ? { stroke: stroke.value } : {},
@@ -5001,7 +5226,9 @@ var decodeDrawStyle = (path, j) => {
5001
5226
  ...fontSize.value !== void 0 ? { fontSize: fontSize.value } : {},
5002
5227
  ...emphasis.value !== void 0 ? { emphasis: emphasis.value } : {},
5003
5228
  ...fontFamily.value !== void 0 ? { fontFamily: fontFamily.value } : {},
5004
- ...markId.value !== void 0 ? { markId: markId.value } : {}
5229
+ ...markId.value !== void 0 ? { markId: markId.value } : {},
5230
+ ...rotation.value !== void 0 ? { rotation: rotation.value } : {},
5231
+ ...tip.value !== void 0 ? { tip: tip.value } : {}
5005
5232
  });
5006
5233
  };
5007
5234
  var decodeCurveCommand = (path, j) => {
@@ -5233,6 +5460,10 @@ var decodeDisplayKind = (path, j) => {
5233
5460
  const r = decodeSkeletonSpec(path, j);
5234
5461
  return r.ok ? ok({ kind: "Skeleton", spec: r.value }) : r;
5235
5462
  }
5463
+ case "Icon": {
5464
+ const r = decodeIconSpec(path, j);
5465
+ return r.ok ? ok({ kind: "Icon", spec: r.value }) : r;
5466
+ }
5236
5467
  case "LabelValueRow": {
5237
5468
  const r = decodeLabelValueRowSpec(path, j);
5238
5469
  return r.ok ? ok({ kind: "LabelValueRow", spec: r.value }) : r;
@@ -5311,6 +5542,14 @@ var decodeFormFieldKind = (autoBind, path, j) => {
5311
5542
  ...onToggleField
5312
5543
  }) : v;
5313
5544
  }
5545
+ case "Toggle": {
5546
+ const v = valueOr(decodeBinding, schema.controlValueDefaults.checkbox, "Toggle value binding");
5547
+ return v.ok ? ok({
5548
+ kind: "Toggle",
5549
+ value: v.value,
5550
+ ...onToggleField
5551
+ }) : v;
5552
+ }
5314
5553
  case "Choice": {
5315
5554
  const options = reqField(
5316
5555
  path,
@@ -5708,7 +5947,7 @@ var decodeTonedPill = (path, f) => {
5708
5947
  };
5709
5948
  return ok(k);
5710
5949
  };
5711
- var decodeCellKindErased = (path, j) => {
5950
+ var decodeCellKindErased = (path, j, columnField) => {
5712
5951
  const fo = requireObject(path, j);
5713
5952
  if (!fo.ok) return fo;
5714
5953
  const f = fo.value;
@@ -5772,7 +6011,15 @@ var decodeCellKindErased = (path, j) => {
5772
6011
  case "TonedPill":
5773
6012
  return decodeTonedPill(path, f);
5774
6013
  case "Progress": {
5775
- const k = { kind: "Progress", fraction: () => 0 };
6014
+ const field2 = columnField;
6015
+ const k = {
6016
+ kind: "Progress",
6017
+ fraction: field2 === void 0 ? () => 0 : (row) => {
6018
+ const v = row !== null && typeof row === "object" ? row[field2] : void 0;
6019
+ const n = typeof v === "number" && Number.isFinite(v) ? v : 0;
6020
+ return Math.max(0, Math.min(1, n));
6021
+ }
6022
+ };
5776
6023
  return ok(k);
5777
6024
  }
5778
6025
  case "Custom": {
@@ -5790,13 +6037,78 @@ var decodeCellKindErased = (path, j) => {
5790
6037
  );
5791
6038
  }
5792
6039
  };
6040
+ var nearMiss = (path, found, canonical) => makeError(
6041
+ "WRONG_TYPE",
6042
+ `${path}.${found}`,
6043
+ `'${found}' is not part of the grid vocabulary \u2014 it would be ignored, not honoured`,
6044
+ canonical
6045
+ );
6046
+ var checkNearMisses = (path, f, candidates) => {
6047
+ for (const [name, canonical] of candidates) {
6048
+ if (tryField(f, name) !== void 0) return nearMiss(path, name, canonical);
6049
+ }
6050
+ return ok(void 0);
6051
+ };
6052
+ var COLUMN_NEAR_MISSES = [
6053
+ // Named by the census row itself. Deliberately NOT aliased to
6054
+ // `editable: false`: an inverting alias that guesses wrong makes a read-only
6055
+ // column editable.
6056
+ ["readOnly", "editable: false \u2014 the column flag NARROWS the grid's editable capability"]
6057
+ ];
6058
+ var GRID_NEAR_MISSES = [
6059
+ // The sharpest of them: a LITERAL page number is not expressible at all,
6060
+ // because the position lives in State so a control can move it. Ignoring it
6061
+ // is what produced the ×11 fake-affordance cluster.
6062
+ [
6063
+ "currentPage",
6064
+ 'pageStateKey \u2014 the page POSITION lives in State as {"page": N} so the pager can move it; a literal page number is not expressible'
6065
+ ],
6066
+ [
6067
+ "page",
6068
+ 'pageStateKey \u2014 the page POSITION lives in State as {"page": N} so the pager can move it; a literal page number is not expressible'
6069
+ ],
6070
+ [
6071
+ "pageIndex",
6072
+ 'pageStateKey \u2014 the page POSITION lives in State as {"page": N}, 1-based (not a zero-based index)'
6073
+ ],
6074
+ [
6075
+ "sortable",
6076
+ "sortStateKey on the grid + sortable on each COLUMN \u2014 grid-wide sortable is the staticRows spelling; a data-bound grid narrows per column"
6077
+ ],
6078
+ [
6079
+ "onEdit",
6080
+ "editStateKey \u2014 the edit DESTINATION is a State key on the grid; onEdit is a per-cell host closure and carries no destination across the wire"
6081
+ ],
6082
+ [
6083
+ "behaviour",
6084
+ "sibling fields on the grid (sortStateKey / pageStateKey / pageSize / editStateKey / defaultSort) \u2014 grid behaviour is not a nested record"
6085
+ ],
6086
+ [
6087
+ "behavior",
6088
+ "sibling fields on the grid (sortStateKey / pageStateKey / pageSize / editStateKey / defaultSort) \u2014 grid behaviour is not a nested record"
6089
+ ]
6090
+ ];
5793
6091
  var decodeColumnErased = (path, j) => {
5794
6092
  const fo = requireObject(path, j);
5795
6093
  if (!fo.ok) return fo;
5796
6094
  const f = fo.value;
5797
6095
  const format = optField(path, f, "format", decodeCellFormat);
5798
6096
  if (!format.ok) return format;
5799
- const kind = reqFieldAliased(path, f, "kind", ["type"], "CellKindErased", decodeCellKindErased);
6097
+ const fieldJ = tryField(f, "field");
6098
+ let field2;
6099
+ if (fieldJ !== void 0) {
6100
+ const fr = requireString(`${path}.field`, fieldJ);
6101
+ if (!fr.ok) return fr;
6102
+ field2 = fr.value;
6103
+ }
6104
+ const kind = reqFieldAliased(
6105
+ path,
6106
+ f,
6107
+ "kind",
6108
+ ["type"],
6109
+ "CellKindErased",
6110
+ (p, v) => decodeCellKindErased(p, v, field2)
6111
+ );
5800
6112
  if (!kind.ok) return kind;
5801
6113
  const label = reqFieldAliased(
5802
6114
  path,
@@ -5810,12 +6122,21 @@ var decodeColumnErased = (path, j) => {
5810
6122
  const width = optField(path, f, "width", decodeColumnWidth);
5811
6123
  if (!width.ok) return width;
5812
6124
  const hasValue = tryField(f, "value") !== void 0;
5813
- const fieldJ = tryField(f, "field");
5814
- let field2;
5815
- if (fieldJ !== void 0) {
5816
- const fr = requireString(`${path}.field`, fieldJ);
5817
- if (!fr.ok) return fr;
5818
- field2 = fr.value;
6125
+ const colNearMiss = checkNearMisses(path, f, COLUMN_NEAR_MISSES);
6126
+ if (!colNearMiss.ok) return colNearMiss;
6127
+ const colEditableJ = tryField(f, "editable");
6128
+ let colEditable;
6129
+ if (colEditableJ !== void 0) {
6130
+ const er = requireBool(`${path}.editable`, colEditableJ);
6131
+ if (!er.ok) return er;
6132
+ colEditable = er.value;
6133
+ }
6134
+ const sortableJ = tryField(f, "sortable");
6135
+ let sortable;
6136
+ if (sortableJ !== void 0) {
6137
+ const sr = requireBool(`${path}.sortable`, sortableJ);
6138
+ if (!sr.ok) return sr;
6139
+ sortable = sr.value;
5819
6140
  }
5820
6141
  return ok({
5821
6142
  format: format.value ?? { kind: "None" },
@@ -5823,9 +6144,27 @@ var decodeColumnErased = (path, j) => {
5823
6144
  label: label.value,
5824
6145
  width: width.value ?? { kind: "Auto" },
5825
6146
  ...hasValue ? { value: () => ({ kind: "Empty" }) } : {},
5826
- ...field2 !== void 0 ? { field: field2 } : {}
6147
+ ...field2 !== void 0 ? { field: field2 } : {},
6148
+ ...sortable !== void 0 ? { sortable } : {},
6149
+ ...colEditable !== void 0 ? { editable: colEditable } : {}
5827
6150
  });
5828
6151
  };
6152
+ var decodeSortDirection = (p, j) => bareEnum(p, j, ["asc", "desc"], "SortDirection");
6153
+ var decodeDefaultSort = (path, j) => {
6154
+ const fo = requireObject(path, j);
6155
+ if (!fo.ok) return fo;
6156
+ const f = fo.value;
6157
+ const columnJ = requireField(path, f, "column", "non-negative header index");
6158
+ if (!columnJ.ok) return columnJ;
6159
+ const cv = columnJ.value;
6160
+ if (cv.kind !== "JNumber" || cv.value < 0 || !Number.isInteger(cv.value))
6161
+ return wrongType(`${path}.column`, "JSON number (non-negative integer header index)");
6162
+ const directionJ = requireField(path, f, "direction", "asc | desc");
6163
+ if (!directionJ.ok) return directionJ;
6164
+ const direction = decodeSortDirection(`${path}.direction`, directionJ.value);
6165
+ if (!direction.ok) return direction;
6166
+ return ok({ column: cv.value, direction: direction.value });
6167
+ };
5829
6168
  var decodeStaticRows = (path, j) => {
5830
6169
  const fo = requireObject(path, j);
5831
6170
  if (!fo.ok) return fo;
@@ -5852,7 +6191,16 @@ var decodeStaticRows = (path, j) => {
5852
6191
  );
5853
6192
  });
5854
6193
  if (!rows.ok) return rows;
5855
- return ok({ headers: headers.value, rows: rows.value });
6194
+ const sortable = optField(path, f, "sortable", requireBool);
6195
+ if (!sortable.ok) return sortable;
6196
+ const defaultSort = optField(path, f, "defaultSort", decodeDefaultSort);
6197
+ if (!defaultSort.ok) return defaultSort;
6198
+ return ok({
6199
+ headers: headers.value,
6200
+ rows: rows.value,
6201
+ ...sortable.value !== void 0 ? { sortable: sortable.value } : {},
6202
+ ...defaultSort.value !== void 0 ? { defaultSort: defaultSort.value } : {}
6203
+ });
5856
6204
  };
5857
6205
  var decodeGridSpec = (path, j) => {
5858
6206
  const fo = requireObject(path, j);
@@ -5887,6 +6235,43 @@ var decodeGridSpec = (path, j) => {
5887
6235
  if (!rk.ok) return rk;
5888
6236
  rowKeyField = rk.value;
5889
6237
  }
6238
+ const sortStateKeyJ = tryField(f, "sortStateKey");
6239
+ let sortStateKey;
6240
+ if (sortStateKeyJ !== void 0) {
6241
+ const sk = requireString(`${path}.sortStateKey`, sortStateKeyJ);
6242
+ if (!sk.ok) return sk;
6243
+ sortStateKey = sk.value;
6244
+ }
6245
+ const pageSizeJ = tryField(f, "pageSize");
6246
+ let pageSize;
6247
+ if (pageSizeJ !== void 0) {
6248
+ if (pageSizeJ.kind !== "JNumber" || pageSizeJ.value < 1 || !Number.isInteger(pageSizeJ.value))
6249
+ return wrongType(`${path}.pageSize`, "JSON number (integer page size of 1 or more)");
6250
+ pageSize = pageSizeJ.value;
6251
+ }
6252
+ const defaultSortJ = tryField(f, "defaultSort");
6253
+ let defaultSort;
6254
+ if (defaultSortJ !== void 0) {
6255
+ const ds = decodeDefaultSort(`${path}.defaultSort`, defaultSortJ);
6256
+ if (!ds.ok) return ds;
6257
+ defaultSort = ds.value;
6258
+ }
6259
+ const gridNearMiss = checkNearMisses(path, f, GRID_NEAR_MISSES);
6260
+ if (!gridNearMiss.ok) return gridNearMiss;
6261
+ const editStateKeyJ = tryField(f, "editStateKey");
6262
+ let editStateKey;
6263
+ if (editStateKeyJ !== void 0) {
6264
+ const ek = requireString(`${path}.editStateKey`, editStateKeyJ);
6265
+ if (!ek.ok) return ek;
6266
+ editStateKey = ek.value;
6267
+ }
6268
+ const pageStateKeyJ = tryField(f, "pageStateKey");
6269
+ let pageStateKey;
6270
+ if (pageStateKeyJ !== void 0) {
6271
+ const pk = requireString(`${path}.pageStateKey`, pageStateKeyJ);
6272
+ if (!pk.ok) return pk;
6273
+ pageStateKey = pk.value;
6274
+ }
5890
6275
  const staticRowsJ = tryField(f, "staticRows");
5891
6276
  let staticRows;
5892
6277
  if (staticRowsJ !== void 0) {
@@ -5901,6 +6286,11 @@ var decodeGridSpec = (path, j) => {
5901
6286
  ...hasRowClick ? { onRowClick: () => placeholderAction } : {},
5902
6287
  ...hasRowKey ? { rowKey: () => CLOSURE2 } : {},
5903
6288
  ...rowKeyField !== void 0 ? { rowKeyField } : {},
6289
+ ...sortStateKey !== void 0 ? { sortStateKey } : {},
6290
+ ...pageSize !== void 0 ? { pageSize } : {},
6291
+ ...pageStateKey !== void 0 ? { pageStateKey } : {},
6292
+ ...defaultSort !== void 0 ? { defaultSort } : {},
6293
+ ...editStateKey !== void 0 ? { editStateKey } : {},
5904
6294
  ...staticRows !== void 0 ? { staticRows } : {}
5905
6295
  });
5906
6296
  };
@@ -5932,6 +6322,20 @@ var decodeChartSpec = (path, j) => {
5932
6322
  if (!yFields.ok) return yFields;
5933
6323
  const title = optField(path, f, "title", decodeTextSource);
5934
6324
  if (!title.ok) return title;
6325
+ const valueFormat = optField(path, f, "valueFormat", decodeFormat);
6326
+ if (!valueFormat.ok) return valueFormat;
6327
+ const xTitle = optField(path, f, "xTitle", decodeTextSource);
6328
+ if (!xTitle.ok) return xTitle;
6329
+ const yTitle = optField(path, f, "yTitle", decodeTextSource);
6330
+ if (!yTitle.ok) return yTitle;
6331
+ const subtitle = optField(path, f, "subtitle", decodeTextSource);
6332
+ if (!subtitle.ok) return subtitle;
6333
+ const legendPosition = optField(path, f, "legendPosition", decodeChartLegendPosition);
6334
+ if (!legendPosition.ok) return legendPosition;
6335
+ const dataLabels = optField(path, f, "dataLabels", decodeChartDataLabels);
6336
+ if (!dataLabels.ok) return dataLabels;
6337
+ const xScale = optField(path, f, "xScale", decodeChartXScale);
6338
+ if (!xScale.ok) return xScale;
5935
6339
  const hasPointClick = tryField(f, "onPointClick") !== void 0;
5936
6340
  const stacked = optField(path, f, "stacked", requireBool);
5937
6341
  if (!stacked.ok) return stacked;
@@ -5942,6 +6346,13 @@ var decodeChartSpec = (path, j) => {
5942
6346
  yFields: yFields.value,
5943
6347
  stacked: stacked.value ?? false,
5944
6348
  ...title.value !== void 0 ? { title: title.value } : {},
6349
+ ...valueFormat.value !== void 0 ? { valueFormat: valueFormat.value } : {},
6350
+ ...xTitle.value !== void 0 ? { xTitle: xTitle.value } : {},
6351
+ ...yTitle.value !== void 0 ? { yTitle: yTitle.value } : {},
6352
+ ...subtitle.value !== void 0 ? { subtitle: subtitle.value } : {},
6353
+ ...legendPosition.value !== void 0 ? { legendPosition: legendPosition.value } : {},
6354
+ ...dataLabels.value !== void 0 ? { dataLabels: dataLabels.value } : {},
6355
+ ...xScale.value !== void 0 ? { xScale: xScale.value } : {},
5945
6356
  ...hasPointClick ? { onPointClick: () => placeholderAction } : {}
5946
6357
  });
5947
6358
  };
@@ -6508,6 +6919,7 @@ var decodeNodeKind = (path, j) => {
6508
6919
  case "Callout":
6509
6920
  case "Progress":
6510
6921
  case "Skeleton":
6922
+ case "Icon":
6511
6923
  case "LabelValueRow":
6512
6924
  case "Fact":
6513
6925
  case "Link":
@@ -6580,8 +6992,22 @@ var decodeNodeKind = (path, j) => {
6580
6992
  return ok({ kind: "ErrorBoundary", spec: { child: child.value, fallback: fallback.value } });
6581
6993
  }
6582
6994
  case "Switch": {
6583
- const stateKey = reqField(path, f, "stateKey", "Switch stateKey string", requireString);
6584
- if (!stateKey.ok) return stateKey;
6995
+ const onJ = tryField(f, "on");
6996
+ const on = onJ !== void 0 ? decodeBinding(`${path}.on`, onJ) : (() => {
6997
+ const stateKey = reqField(
6998
+ path,
6999
+ f,
7000
+ "stateKey",
7001
+ "Switch stateKey string",
7002
+ requireString
7003
+ );
7004
+ return stateKey.ok ? ok({
7005
+ kind: "State",
7006
+ key: stateKey.value,
7007
+ defaultValue: void 0
7008
+ }) : stateKey;
7009
+ })();
7010
+ if (!on.ok) return on;
6585
7011
  const casesArr = reqField(path, f, "cases", "Switch cases array", requireArray);
6586
7012
  if (!casesArr.ok) return casesArr;
6587
7013
  const cases = traverseIndexed(casesArr.value, (i, item) => {
@@ -6599,7 +7025,7 @@ var decodeNodeKind = (path, j) => {
6599
7025
  if (!def.ok) return def;
6600
7026
  return ok({
6601
7027
  kind: "Switch",
6602
- spec: { stateKey: stateKey.value, cases: cases.value, default: def.value }
7028
+ spec: { on: on.value, cases: cases.value, default: def.value }
6603
7029
  });
6604
7030
  }
6605
7031
  case "FragmentDecl": {
@@ -7004,7 +7430,11 @@ var coerce = {
7004
7430
  emphasisFlag: (v) => viaAst(v, decodeEmphasisFlag),
7005
7431
  headingVariant: (v) => viaAst(v, decodeHeadingVariant),
7006
7432
  badgeVariant: (v) => viaAst(v, decodeBadgeVariant),
7007
- iconSource: (v) => viaAst(v, decodeIconSource)
7433
+ iconSource: (v) => viaAst(v, decodeIconSource),
7434
+ /** `Icon.Size : IconSize` (Phase 821) — the UpdateProp twin of `decodeIconSize`,
7435
+ * added with the standalone Icon display kind. */
7436
+ iconSize: (v) => viaAst(v, decodeIconSize),
7437
+ stringOption: (v) => viaAst(v, parseStaticStringOpt)
7008
7438
  };
7009
7439
  var invalidJson = (parseMessage) => makeError(
7010
7440
  "INVALID_JSON",
@@ -7242,6 +7672,37 @@ var updateField = (field2, value, kind) => {
7242
7672
  }
7243
7673
  case "Skeleton":
7244
7674
  return field2 === "Rows" ? patch(coerce.int(value), (x) => disp({ kind: "Skeleton", spec: { rows: x } })) : { tag: "unknownField" };
7675
+ case "Icon": {
7676
+ const s = d.spec;
7677
+ switch (field2) {
7678
+ case "Icon":
7679
+ return patch(
7680
+ coerce.string(value),
7681
+ (x) => disp({ kind: "Icon", spec: { ...s, icon: x } })
7682
+ );
7683
+ case "Size":
7684
+ return patch(
7685
+ coerce.iconSize(value),
7686
+ (x) => disp({ kind: "Icon", spec: { ...s, size: x } })
7687
+ );
7688
+ case "Tone":
7689
+ return patch(
7690
+ coerce.tone(value),
7691
+ (x) => disp({ kind: "Icon", spec: { ...s, tone: x } })
7692
+ );
7693
+ case "Label": {
7694
+ return patch(coerce.stringOption(value), (x) => {
7695
+ const { label: _label, ...rest } = s;
7696
+ return disp({
7697
+ kind: "Icon",
7698
+ spec: { ...rest, ...x !== void 0 ? { label: x } : {} }
7699
+ });
7700
+ });
7701
+ }
7702
+ default:
7703
+ return { tag: "unknownField" };
7704
+ }
7705
+ }
7245
7706
  case "Callout": {
7246
7707
  const s = d.spec;
7247
7708
  switch (field2) {
@@ -8049,6 +8510,168 @@ var apply = (tree, op) => {
8049
8510
  return { ok: true, value: { newTree: r.value, emittedTelemetry: telem } };
8050
8511
  };
8051
8512
 
8513
+ // src/placement.ts
8514
+ var derivedFreshIds = (oldId, taken) => {
8515
+ for (let n = 1; ; n += 1) {
8516
+ const candidate = n === 1 ? `${oldId}-copy` : `${oldId}-copy-${n}`;
8517
+ if (!taken(candidate)) return candidate;
8518
+ }
8519
+ };
8520
+ var sequentialFreshIds = (prefix) => {
8521
+ let counter = 0;
8522
+ return (_oldId, taken) => {
8523
+ for (; ; ) {
8524
+ counter += 1;
8525
+ const candidate = `${prefix}-${counter}`;
8526
+ if (!taken(candidate)) return candidate;
8527
+ }
8528
+ };
8529
+ };
8530
+ var ok3 = (value) => ({ ok: true, value });
8531
+ var err = (error) => ({ ok: false, error });
8532
+ var idOf2 = (n) => n.id;
8533
+ var sameOrder = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
8534
+ var containerChildren = (root, parentId) => {
8535
+ const parent = findNode(parentId, root);
8536
+ if (parent === void 0) return err({ kind: "ParentNotFound", parentId });
8537
+ const children = layoutChildren(parent);
8538
+ if (children === void 0) return err({ kind: "ChildlessKind", parentId });
8539
+ return ok3(children.map(idOf2));
8540
+ };
8541
+ var reposition = (order, moved, placement) => {
8542
+ const rest = order.filter((id) => id !== moved);
8543
+ const anchored = (anchor, offset) => {
8544
+ const i = rest.findIndex((id) => id === anchor);
8545
+ if (i < 0) return err({ kind: "UnknownAnchor", anchor });
8546
+ const at = i + offset;
8547
+ return ok3([...rest.slice(0, at), moved, ...rest.slice(at)]);
8548
+ };
8549
+ switch (placement.kind) {
8550
+ case "Last":
8551
+ return ok3([...rest, moved]);
8552
+ case "First":
8553
+ return ok3([moved, ...rest]);
8554
+ case "Before":
8555
+ return anchored(placement.anchor, 0);
8556
+ case "After":
8557
+ return anchored(placement.anchor, 1);
8558
+ }
8559
+ };
8560
+ var structurallyPresent = (nodeId, root) => idOf2(root) === nodeId || findLayoutParent(nodeId, root) !== void 0;
8561
+ var firstSharedId = (root, incoming) => {
8562
+ const existing = new Set(allNodeIds(root));
8563
+ return allNodeIds(incoming).find((id) => existing.has(id));
8564
+ };
8565
+ var canPlace = (root, moved, target) => {
8566
+ const r = root;
8567
+ if (!structurallyPresent(moved, r)) return err({ kind: "NodeNotFound", nodeId: moved });
8568
+ if (target.parentId === moved) return err({ kind: "MoveIntoSelf", nodeId: moved });
8569
+ if (isAncestor(moved, target.parentId, r))
8570
+ return err({ kind: "MoveIntoDescendant", nodeId: moved, parentId: target.parentId });
8571
+ const siblings = containerChildren(r, target.parentId);
8572
+ if (!siblings.ok) return siblings;
8573
+ const membership = [...siblings.value.filter((id) => id !== moved), moved];
8574
+ const placed = reposition(membership, moved, target.placement);
8575
+ return placed.ok ? ok3(void 0) : placed;
8576
+ };
8577
+ var placeOp = (root, child, target) => {
8578
+ const r = root;
8579
+ const siblings = containerChildren(r, target.parentId);
8580
+ if (!siblings.ok) return siblings;
8581
+ const dup = firstSharedId(r, child);
8582
+ if (dup !== void 0) return err({ kind: "DuplicateId", nodeId: dup });
8583
+ const childId = idOf2(child);
8584
+ const appended = [...siblings.value, childId];
8585
+ const wanted = reposition(appended, childId, target.placement);
8586
+ if (!wanted.ok) return wanted;
8587
+ const insert = { kind: "InsertChild", parentId: target.parentId, child };
8588
+ return ok3(
8589
+ sameOrder(wanted.value, appended) ? insert : {
8590
+ kind: "Batch",
8591
+ ops: [
8592
+ insert,
8593
+ { kind: "ReorderChildren", parentId: target.parentId, newOrder: wanted.value }
8594
+ ]
8595
+ }
8596
+ );
8597
+ };
8598
+ var moveOp = (root, moved, target) => {
8599
+ const legal = canPlace(root, moved, target);
8600
+ if (!legal.ok) return legal;
8601
+ const siblings = containerChildren(root, target.parentId);
8602
+ if (!siblings.ok) return siblings;
8603
+ const appended = [...siblings.value.filter((id) => id !== moved), moved];
8604
+ const wanted = reposition(appended, moved, target.placement);
8605
+ if (!wanted.ok) return wanted;
8606
+ const move = { kind: "MoveNode", target: moved, newParentId: target.parentId };
8607
+ return ok3(
8608
+ sameOrder(wanted.value, appended) ? move : {
8609
+ kind: "Batch",
8610
+ ops: [
8611
+ move,
8612
+ { kind: "ReorderChildren", parentId: target.parentId, newOrder: wanted.value }
8613
+ ]
8614
+ }
8615
+ );
8616
+ };
8617
+ var nudgeOp = (root, nodeId, delta) => {
8618
+ const r = root;
8619
+ if (idOf2(r) === nodeId) return err({ kind: "CannotNudgeRoot", nodeId });
8620
+ const parent = findLayoutParent(nodeId, r);
8621
+ if (parent === void 0) return err({ kind: "NodeNotFound", nodeId });
8622
+ const ids = (layoutChildren(parent) ?? []).map(idOf2);
8623
+ const index = ids.findIndex((id) => id === nodeId);
8624
+ const swapWith = index + delta;
8625
+ if (swapWith < 0 || swapWith >= ids.length)
8626
+ return err({ kind: "NudgeOutOfRange", nodeId, delta });
8627
+ const reordered = ids.map(
8628
+ (id, i) => i === index ? ids[swapWith] : i === swapWith ? ids[index] : id
8629
+ );
8630
+ return ok3({ kind: "ReorderChildren", parentId: idOf2(parent), newOrder: reordered });
8631
+ };
8632
+ var mapImmediate = (n, f) => {
8633
+ let cur = n;
8634
+ const count = childSlots(n).length;
8635
+ for (let i = 0; i < count; i += 1) {
8636
+ const slot = childSlots(cur)[i];
8637
+ const mapped = f(slot.child);
8638
+ if (mapped !== slot.child) cur = slot.rebuild(mapped);
8639
+ }
8640
+ return cur;
8641
+ };
8642
+ var remapForInsert = (freshIds, targetRoot, incoming) => {
8643
+ const existing = new Set(allNodeIds(targetRoot));
8644
+ const taken = /* @__PURE__ */ new Set([...existing, ...allNodeIds(incoming)]);
8645
+ const rename = /* @__PURE__ */ new Map();
8646
+ for (const oldId of allNodeIds(incoming)) {
8647
+ if (existing.has(oldId)) {
8648
+ const fresh = freshIds(oldId, (candidate) => taken.has(candidate));
8649
+ taken.add(fresh);
8650
+ rename.set(oldId, fresh);
8651
+ }
8652
+ }
8653
+ if (rename.size === 0) return incoming;
8654
+ const rewrite = (node2) => {
8655
+ const withChildren = mapImmediate(node2, rewrite);
8656
+ const fresh = rename.get(withChildren.id);
8657
+ return fresh === void 0 ? withChildren : { ...withChildren, id: fresh };
8658
+ };
8659
+ return rewrite(incoming);
8660
+ };
8661
+ var duplicateOpWith = (freshIds, root, source, target) => {
8662
+ const r = root;
8663
+ const sub = findNode(source, r);
8664
+ if (sub === void 0) return err({ kind: "NodeNotFound", nodeId: source });
8665
+ return placeOp(root, remapForInsert(freshIds, r, sub), target);
8666
+ };
8667
+ var duplicateOp = (root, source, target) => duplicateOpWith(derivedFreshIds, root, source, target);
8668
+ var pasteOpWith = (freshIds, targetRoot, incoming, target) => placeOp(
8669
+ targetRoot,
8670
+ remapForInsert(freshIds, targetRoot, incoming),
8671
+ target
8672
+ );
8673
+ var pasteOp = (targetRoot, incoming, target) => pasteOpWith(derivedFreshIds, targetRoot, incoming, target);
8674
+
8052
8675
  // src/dag.ts
8053
8676
  var str2 = (s) => {
8054
8677
  let out = '"';
@@ -9146,6 +9769,7 @@ exports.PAYLOAD_KEY = PAYLOAD_KEY;
9146
9769
  exports.PROFILE_KEY = PROFILE_KEY;
9147
9770
  exports.REQUIRED_PROFILE_KEY = REQUIRED_PROFILE_KEY;
9148
9771
  exports.apply = apply;
9772
+ exports.canPlace = canPlace;
9149
9773
  exports.cellString = cellString;
9150
9774
  exports.coerce = coerce;
9151
9775
  exports.coreV1 = coreV1;
@@ -9158,6 +9782,9 @@ exports.decodeNode = decodeNode;
9158
9782
  exports.decodeNodeTolerant = decodeNodeTolerant;
9159
9783
  exports.decodeOp = decodeOp;
9160
9784
  exports.decodeTolerant = decodeTolerant;
9785
+ exports.derivedFreshIds = derivedFreshIds;
9786
+ exports.duplicateOp = duplicateOp;
9787
+ exports.duplicateOpWith = duplicateOpWith;
9161
9788
  exports.encodeCell = encodeCell;
9162
9789
  exports.encodeColExpr = encodeColExpr;
9163
9790
  exports.encodeDagRecord = encodeDagRecord;
@@ -9175,15 +9802,22 @@ exports.evalPipelineWith = evalPipelineWith;
9175
9802
  exports.evalPipelineWithInEnv = evalPipelineWithInEnv;
9176
9803
  exports.evalSource = evalSource;
9177
9804
  exports.jsonField = field;
9805
+ exports.liveValueToTable = liveValueToTable;
9178
9806
  exports.merge3Way = merge3Way;
9807
+ exports.moveOp = moveOp;
9179
9808
  exports.negotiate = negotiate;
9180
9809
  exports.negotiateEnvelope = negotiateEnvelope;
9181
9810
  exports.noResolve = noResolve;
9811
+ exports.nudgeOp = nudgeOp;
9182
9812
  exports.parse = parse;
9813
+ exports.pasteOp = pasteOp;
9814
+ exports.pasteOpWith = pasteOpWith;
9183
9815
  exports.pipelineParams = pipelineParams;
9816
+ exports.placeOp = placeOp;
9184
9817
  exports.reencodeNode = reencodeNode;
9185
9818
  exports.renderAstCanonical = renderAstCanonical;
9186
9819
  exports.renderProfile = renderProfile;
9820
+ exports.sequentialFreshIds = sequentialFreshIds;
9187
9821
  exports.stepParams = stepParams;
9188
9822
  exports.tryParseProfile = tryParseProfile;
9189
9823
  exports.validateAnswer = validateAnswer;