@fuaran-ui/ops 0.6.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
@@ -430,7 +430,10 @@ var binding = (b, staticEnc = objValue) => {
430
430
  return caseObj("Transform", [
431
431
  ...paramFields,
432
432
  ["pipeline", jArray(b.pipeline.map(transformStep))],
433
- ["source", dataSource(b.source)]
433
+ [
434
+ "source",
435
+ b.source.kind === "Data" ? dataSource(b.source.source) : binding(b.source.binding, jsonValue)
436
+ ]
434
437
  ]);
435
438
  }
436
439
  case "Invoke":
@@ -464,11 +467,12 @@ var action = (a) => {
464
467
  ]);
465
468
  case "Navigate":
466
469
  return caseObj("Navigate", [["route", str(a.route)]]);
467
- case "SetState":
468
- return caseObj("SetState", [
469
- ["key", str(a.key)],
470
- ["value", jsonValue(a.value)]
471
- ]);
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
+ }
472
476
  case "AiTool":
473
477
  return caseObj("AiTool", [
474
478
  ["args", jsonValue(a.args)],
@@ -524,6 +528,13 @@ var cellFormat = (f) => {
524
528
  return caseObj("SignificantDigits", [["digits", num(f.digits)]]);
525
529
  case "Date":
526
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)]]);
527
538
  case "Custom":
528
539
  return caseObj("Custom", [["fn", CLOSURE]]);
529
540
  default:
@@ -542,6 +553,11 @@ var formatIntent = (f) => {
542
553
  return caseObj("Date", [["dateStyle", str(f.dateStyle)]]);
543
554
  case "RelativeTime":
544
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
+ ]);
545
561
  default:
546
562
  return assertNever(f);
547
563
  }
@@ -637,6 +653,7 @@ var linkSpec = (s) => {
637
653
  ];
638
654
  if (s.rel !== void 0) fields.push(["rel", str(s.rel)]);
639
655
  if (s.target !== void 0) fields.push(["target", str(s.target)]);
656
+ if (s.protection !== void 0) fields.push(["protection", str(s.protection)]);
640
657
  return jObject(fields);
641
658
  };
642
659
  var imageSpec = (s) => jObject([
@@ -674,6 +691,13 @@ var mathSpec = (s) => jObject([
674
691
  ]);
675
692
  var sparklineSpec = (s) => jObject([["source", binding(s.source, staticFloatSeq)]]);
676
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
+ };
677
701
  var calloutSpec = (s) => {
678
702
  const fields = [
679
703
  ["body", textSource(s.body)],
@@ -717,6 +741,8 @@ var drawStyle = (s) => {
717
741
  if (s.emphasis !== void 0) fields.push(["emphasis", str(s.emphasis)]);
718
742
  if (s.fontFamily !== void 0) fields.push(["fontFamily", str(s.fontFamily)]);
719
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)]);
720
746
  return jObject(fields);
721
747
  };
722
748
  var curveCommand = (c) => {
@@ -833,6 +859,8 @@ var displayKind = (d) => {
833
859
  return hoistSpec("Progress", progressSpec(d.spec));
834
860
  case "Skeleton":
835
861
  return hoistSpec("Skeleton", skeletonSpec(d.spec));
862
+ case "Icon":
863
+ return hoistSpec("Icon", iconSpec(d.spec));
836
864
  case "LabelValueRow":
837
865
  return hoistSpec("LabelValueRow", labelValueRowSpec(d.spec));
838
866
  case "Fact":
@@ -1140,8 +1168,14 @@ var columnErased = (c) => {
1140
1168
  pushColumnWidthOptional(fields, "width", c.width);
1141
1169
  if (c.value !== void 0) fields.push(["value", CLOSURE]);
1142
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)]);
1143
1173
  return jObject(fields);
1144
1174
  };
1175
+ var defaultSortJson = (d) => jObject([
1176
+ ["column", intLit(d.column)],
1177
+ ["direction", str(d.direction)]
1178
+ ]);
1145
1179
  var gridSpec = (s) => {
1146
1180
  const fields = [
1147
1181
  ["columns", jArray(s.columns.map(columnErased))],
@@ -1153,14 +1187,22 @@ var gridSpec = (s) => {
1153
1187
  if (s.onRowClick !== void 0) fields.push(["onRowClick", CLOSURE]);
1154
1188
  if (s.rowKey !== void 0) fields.push(["rowKey", CLOSURE]);
1155
1189
  if (s.rowKeyField !== void 0) fields.push(["rowKeyField", str(s.rowKeyField)]);
1156
- if (s.staticRows !== void 0)
1157
- fields.push([
1158
- "staticRows",
1159
- jObject([
1160
- ["headers", jArray(s.staticRows.headers.map(textSource))],
1161
- ["rows", jArray(s.staticRows.rows.map((row) => jArray(row.map(textSource))))]
1162
- ])
1163
- ]);
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
+ }
1164
1206
  return jObject(fields);
1165
1207
  };
1166
1208
  var chartSpec = (s) => {
@@ -1173,6 +1215,13 @@ var chartSpec = (s) => {
1173
1215
  ["yFields", jArray(s.yFields.map(str))]
1174
1216
  ];
1175
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)]);
1176
1225
  if (s.onPointClick !== void 0) fields.push(["onPointClick", CLOSURE]);
1177
1226
  return jObject(fields);
1178
1227
  };
@@ -3091,12 +3140,23 @@ var decodeTextAnchor = (p, j) => bareEnum(p, j, ["Start", "Middle", "End"], "Tex
3091
3140
  var decodeStyleRole = (p, j) => bareEnum(p, j, ["None", "Eyebrow", "Data", "Lede", "Caption"], "StyleRole");
3092
3141
  var decodeFontVoice = (p, j) => bareEnum(p, j, ["Default", "Display", "Structural"], "FontVoice");
3093
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");
3094
3146
  var decodeFileReadEncoding = (p, j) => bareEnum(p, j, ["Text", "Base64", "DataUrl"], "FileReadEncoding");
3095
3147
  var decodeAriaRole = (p, j) => {
3096
3148
  if (j.kind !== "JString") return wrongType(p, "JSON string (ARIA role)");
3097
3149
  return ok(j.value);
3098
3150
  };
3099
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
+ );
3100
3160
  var decodeCellFormat = (path, j) => {
3101
3161
  const fo = requireObject(path, j);
3102
3162
  if (!fo.ok) return fo;
@@ -3131,13 +3191,24 @@ var decodeCellFormat = (path, j) => {
3131
3191
  const r = reqField(path, f, "format", "format string", requireString);
3132
3192
  return r.ok ? ok({ kind: "Date", format: r.value }) : r;
3133
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
+ }
3134
3205
  case "Custom":
3135
3206
  return ok({ kind: "Custom", format: () => CLOSURE2 });
3136
3207
  default:
3137
3208
  return unknownDuCase(
3138
3209
  path,
3139
3210
  d.value,
3140
- "None | Number | Currency | Percent | SignificantDigits | Date | Custom"
3211
+ "None | Number | Currency | Percent | SignificantDigits | Date | Duration | RelativeTime | Custom"
3141
3212
  );
3142
3213
  }
3143
3214
  };
@@ -3175,22 +3246,22 @@ var decodeFormat = (path, j) => {
3175
3246
  return r.ok ? ok({ kind: "Date", dateStyle: r.value }) : r;
3176
3247
  }
3177
3248
  case "RelativeTime": {
3178
- const r = reqField(
3179
- path,
3180
- f,
3181
- "unit",
3182
- "RelativeTimeUnit string",
3183
- (p, v) => bareEnum(
3184
- p,
3185
- v,
3186
- ["Second", "Minute", "Hour", "Day", "Week", "Month", "Year"],
3187
- "RelativeTimeUnit"
3188
- )
3189
- );
3249
+ const r = reqField(path, f, "unit", "RelativeTimeUnit string", decodeRelativeTimeUnit);
3190
3250
  return r.ok ? ok({ kind: "RelativeTime", unit: r.value }) : r;
3191
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
+ }
3192
3259
  default:
3193
- 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
+ );
3194
3265
  }
3195
3266
  };
3196
3267
  var decodeLocaleSource = (path, j) => {
@@ -4244,8 +4315,35 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4244
4315
  if (!srcJ.ok) return srcJ;
4245
4316
  const pipeJ = requireField(path, f, "pipeline", "Transform pipeline array");
4246
4317
  if (!pipeJ.ok) return pipeJ;
4247
- const src = decodeDataSource(srcJ.value);
4248
- 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
+ }
4249
4347
  const pipe = decodePipelineCore(pipeJ.value);
4250
4348
  if (!pipe.ok) return makeError("WRONG_TYPE", `${path}.pipeline`, pipe.error);
4251
4349
  const paramsField = tryField(f, "params");
@@ -4296,7 +4394,7 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4296
4394
  }
4297
4395
  const b = {
4298
4396
  kind: "Transform",
4299
- source: src.value,
4397
+ source,
4300
4398
  pipeline: pipe.value,
4301
4399
  ...params !== void 0 ? { params } : {}
4302
4400
  };
@@ -4331,6 +4429,62 @@ var decodeBinding = (path, j, parseStatic = (_p, v) => ok(decodeAstValue(v)), pl
4331
4429
  );
4332
4430
  }
4333
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
+ };
4334
4488
  var decodeBindingArgs = (path, j) => {
4335
4489
  const fo = requireObject(path, j);
4336
4490
  if (!fo.ok) return fo;
@@ -4595,9 +4749,32 @@ var decodeAction = (path, j) => {
4595
4749
  case "SetState": {
4596
4750
  const key = reqField(path, f, "key", "state key string", requireString);
4597
4751
  if (!key.ok) return key;
4598
- const valueJ = requireField(path, f, "value", "JsonValue value");
4599
- if (!valueJ.ok) return valueJ;
4600
- 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);
4601
4778
  return value.ok ? ok({ kind: "SetState", key: key.value, value: value.value }) : value;
4602
4779
  }
4603
4780
  case "AiTool": {
@@ -4806,14 +4983,18 @@ var decodeLinkSpec = (path, j) => {
4806
4983
  if (!rel.ok) return rel;
4807
4984
  const target = optField(path, f, "target", requireString);
4808
4985
  if (!target.ok) return target;
4986
+ const protection = optField(path, f, "protection", decodeLinkProtection);
4987
+ if (!protection.ok) return protection;
4809
4988
  return ok({
4810
4989
  href: href.value,
4811
4990
  label: label.value,
4812
4991
  download: download.value,
4813
4992
  ...rel.value !== void 0 ? { rel: rel.value } : {},
4814
- ...target.value !== void 0 ? { target: target.value } : {}
4993
+ ...target.value !== void 0 ? { target: target.value } : {},
4994
+ ...protection.value !== void 0 ? { protection: protection.value } : {}
4815
4995
  });
4816
4996
  };
4997
+ var decodeLinkProtection = (p, j) => bareEnum(p, j, ["email"], "LinkProtection");
4817
4998
  var decodeImageSpec = (path, j) => {
4818
4999
  const fo = requireObject(path, j);
4819
5000
  if (!fo.ok) return fo;
@@ -4920,6 +5101,28 @@ var decodeSkeletonSpec = (path, j) => {
4920
5101
  const rows = reqField(path, fo.value, "rows", "skeleton row count integer", requireInt);
4921
5102
  return rows.ok ? ok({ rows: rows.value }) : rows;
4922
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
+ };
4923
5126
  var decodeCalloutSpec = (path, j) => {
4924
5127
  const fo = requireObject(path, j);
4925
5128
  if (!fo.ok) return fo;
@@ -5010,6 +5213,10 @@ var decodeDrawStyle = (path, j) => {
5010
5213
  if (!fontFamily.ok) return fontFamily;
5011
5214
  const markId = optField(path, f, "markId", requireString);
5012
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;
5013
5220
  return ok({
5014
5221
  ...fill.value !== void 0 ? { fill: fill.value } : {},
5015
5222
  ...stroke.value !== void 0 ? { stroke: stroke.value } : {},
@@ -5019,7 +5226,9 @@ var decodeDrawStyle = (path, j) => {
5019
5226
  ...fontSize.value !== void 0 ? { fontSize: fontSize.value } : {},
5020
5227
  ...emphasis.value !== void 0 ? { emphasis: emphasis.value } : {},
5021
5228
  ...fontFamily.value !== void 0 ? { fontFamily: fontFamily.value } : {},
5022
- ...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 } : {}
5023
5232
  });
5024
5233
  };
5025
5234
  var decodeCurveCommand = (path, j) => {
@@ -5251,6 +5460,10 @@ var decodeDisplayKind = (path, j) => {
5251
5460
  const r = decodeSkeletonSpec(path, j);
5252
5461
  return r.ok ? ok({ kind: "Skeleton", spec: r.value }) : r;
5253
5462
  }
5463
+ case "Icon": {
5464
+ const r = decodeIconSpec(path, j);
5465
+ return r.ok ? ok({ kind: "Icon", spec: r.value }) : r;
5466
+ }
5254
5467
  case "LabelValueRow": {
5255
5468
  const r = decodeLabelValueRowSpec(path, j);
5256
5469
  return r.ok ? ok({ kind: "LabelValueRow", spec: r.value }) : r;
@@ -5734,7 +5947,7 @@ var decodeTonedPill = (path, f) => {
5734
5947
  };
5735
5948
  return ok(k);
5736
5949
  };
5737
- var decodeCellKindErased = (path, j) => {
5950
+ var decodeCellKindErased = (path, j, columnField) => {
5738
5951
  const fo = requireObject(path, j);
5739
5952
  if (!fo.ok) return fo;
5740
5953
  const f = fo.value;
@@ -5798,7 +6011,15 @@ var decodeCellKindErased = (path, j) => {
5798
6011
  case "TonedPill":
5799
6012
  return decodeTonedPill(path, f);
5800
6013
  case "Progress": {
5801
- 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
+ };
5802
6023
  return ok(k);
5803
6024
  }
5804
6025
  case "Custom": {
@@ -5816,13 +6037,78 @@ var decodeCellKindErased = (path, j) => {
5816
6037
  );
5817
6038
  }
5818
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
+ ];
5819
6091
  var decodeColumnErased = (path, j) => {
5820
6092
  const fo = requireObject(path, j);
5821
6093
  if (!fo.ok) return fo;
5822
6094
  const f = fo.value;
5823
6095
  const format = optField(path, f, "format", decodeCellFormat);
5824
6096
  if (!format.ok) return format;
5825
- 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
+ );
5826
6112
  if (!kind.ok) return kind;
5827
6113
  const label = reqFieldAliased(
5828
6114
  path,
@@ -5836,12 +6122,21 @@ var decodeColumnErased = (path, j) => {
5836
6122
  const width = optField(path, f, "width", decodeColumnWidth);
5837
6123
  if (!width.ok) return width;
5838
6124
  const hasValue = tryField(f, "value") !== void 0;
5839
- const fieldJ = tryField(f, "field");
5840
- let field2;
5841
- if (fieldJ !== void 0) {
5842
- const fr = requireString(`${path}.field`, fieldJ);
5843
- if (!fr.ok) return fr;
5844
- 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;
5845
6140
  }
5846
6141
  return ok({
5847
6142
  format: format.value ?? { kind: "None" },
@@ -5849,9 +6144,27 @@ var decodeColumnErased = (path, j) => {
5849
6144
  label: label.value,
5850
6145
  width: width.value ?? { kind: "Auto" },
5851
6146
  ...hasValue ? { value: () => ({ kind: "Empty" }) } : {},
5852
- ...field2 !== void 0 ? { field: field2 } : {}
6147
+ ...field2 !== void 0 ? { field: field2 } : {},
6148
+ ...sortable !== void 0 ? { sortable } : {},
6149
+ ...colEditable !== void 0 ? { editable: colEditable } : {}
5853
6150
  });
5854
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
+ };
5855
6168
  var decodeStaticRows = (path, j) => {
5856
6169
  const fo = requireObject(path, j);
5857
6170
  if (!fo.ok) return fo;
@@ -5878,7 +6191,16 @@ var decodeStaticRows = (path, j) => {
5878
6191
  );
5879
6192
  });
5880
6193
  if (!rows.ok) return rows;
5881
- 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
+ });
5882
6204
  };
5883
6205
  var decodeGridSpec = (path, j) => {
5884
6206
  const fo = requireObject(path, j);
@@ -5913,6 +6235,43 @@ var decodeGridSpec = (path, j) => {
5913
6235
  if (!rk.ok) return rk;
5914
6236
  rowKeyField = rk.value;
5915
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
+ }
5916
6275
  const staticRowsJ = tryField(f, "staticRows");
5917
6276
  let staticRows;
5918
6277
  if (staticRowsJ !== void 0) {
@@ -5927,6 +6286,11 @@ var decodeGridSpec = (path, j) => {
5927
6286
  ...hasRowClick ? { onRowClick: () => placeholderAction } : {},
5928
6287
  ...hasRowKey ? { rowKey: () => CLOSURE2 } : {},
5929
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 } : {},
5930
6294
  ...staticRows !== void 0 ? { staticRows } : {}
5931
6295
  });
5932
6296
  };
@@ -5958,6 +6322,20 @@ var decodeChartSpec = (path, j) => {
5958
6322
  if (!yFields.ok) return yFields;
5959
6323
  const title = optField(path, f, "title", decodeTextSource);
5960
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;
5961
6339
  const hasPointClick = tryField(f, "onPointClick") !== void 0;
5962
6340
  const stacked = optField(path, f, "stacked", requireBool);
5963
6341
  if (!stacked.ok) return stacked;
@@ -5968,6 +6346,13 @@ var decodeChartSpec = (path, j) => {
5968
6346
  yFields: yFields.value,
5969
6347
  stacked: stacked.value ?? false,
5970
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 } : {},
5971
6356
  ...hasPointClick ? { onPointClick: () => placeholderAction } : {}
5972
6357
  });
5973
6358
  };
@@ -6534,6 +6919,7 @@ var decodeNodeKind = (path, j) => {
6534
6919
  case "Callout":
6535
6920
  case "Progress":
6536
6921
  case "Skeleton":
6922
+ case "Icon":
6537
6923
  case "LabelValueRow":
6538
6924
  case "Fact":
6539
6925
  case "Link":
@@ -7044,7 +7430,11 @@ var coerce = {
7044
7430
  emphasisFlag: (v) => viaAst(v, decodeEmphasisFlag),
7045
7431
  headingVariant: (v) => viaAst(v, decodeHeadingVariant),
7046
7432
  badgeVariant: (v) => viaAst(v, decodeBadgeVariant),
7047
- 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)
7048
7438
  };
7049
7439
  var invalidJson = (parseMessage) => makeError(
7050
7440
  "INVALID_JSON",
@@ -7282,6 +7672,37 @@ var updateField = (field2, value, kind) => {
7282
7672
  }
7283
7673
  case "Skeleton":
7284
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
+ }
7285
7706
  case "Callout": {
7286
7707
  const s = d.spec;
7287
7708
  switch (field2) {
@@ -8089,6 +8510,168 @@ var apply = (tree, op) => {
8089
8510
  return { ok: true, value: { newTree: r.value, emittedTelemetry: telem } };
8090
8511
  };
8091
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
+
8092
8675
  // src/dag.ts
8093
8676
  var str2 = (s) => {
8094
8677
  let out = '"';
@@ -9186,6 +9769,7 @@ exports.PAYLOAD_KEY = PAYLOAD_KEY;
9186
9769
  exports.PROFILE_KEY = PROFILE_KEY;
9187
9770
  exports.REQUIRED_PROFILE_KEY = REQUIRED_PROFILE_KEY;
9188
9771
  exports.apply = apply;
9772
+ exports.canPlace = canPlace;
9189
9773
  exports.cellString = cellString;
9190
9774
  exports.coerce = coerce;
9191
9775
  exports.coreV1 = coreV1;
@@ -9198,6 +9782,9 @@ exports.decodeNode = decodeNode;
9198
9782
  exports.decodeNodeTolerant = decodeNodeTolerant;
9199
9783
  exports.decodeOp = decodeOp;
9200
9784
  exports.decodeTolerant = decodeTolerant;
9785
+ exports.derivedFreshIds = derivedFreshIds;
9786
+ exports.duplicateOp = duplicateOp;
9787
+ exports.duplicateOpWith = duplicateOpWith;
9201
9788
  exports.encodeCell = encodeCell;
9202
9789
  exports.encodeColExpr = encodeColExpr;
9203
9790
  exports.encodeDagRecord = encodeDagRecord;
@@ -9215,15 +9802,22 @@ exports.evalPipelineWith = evalPipelineWith;
9215
9802
  exports.evalPipelineWithInEnv = evalPipelineWithInEnv;
9216
9803
  exports.evalSource = evalSource;
9217
9804
  exports.jsonField = field;
9805
+ exports.liveValueToTable = liveValueToTable;
9218
9806
  exports.merge3Way = merge3Way;
9807
+ exports.moveOp = moveOp;
9219
9808
  exports.negotiate = negotiate;
9220
9809
  exports.negotiateEnvelope = negotiateEnvelope;
9221
9810
  exports.noResolve = noResolve;
9811
+ exports.nudgeOp = nudgeOp;
9222
9812
  exports.parse = parse;
9813
+ exports.pasteOp = pasteOp;
9814
+ exports.pasteOpWith = pasteOpWith;
9223
9815
  exports.pipelineParams = pipelineParams;
9816
+ exports.placeOp = placeOp;
9224
9817
  exports.reencodeNode = reencodeNode;
9225
9818
  exports.renderAstCanonical = renderAstCanonical;
9226
9819
  exports.renderProfile = renderProfile;
9820
+ exports.sequentialFreshIds = sequentialFreshIds;
9227
9821
  exports.stepParams = stepParams;
9228
9822
  exports.tryParseProfile = tryParseProfile;
9229
9823
  exports.validateAnswer = validateAnswer;