@almadar/ui 5.27.0 → 5.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22176,93 +22176,66 @@ var init_DataList = __esm({
22176
22176
  DataList.displayName = "DataList";
22177
22177
  }
22178
22178
  });
22179
- function isTraitConfigObject(v) {
22180
- return v !== null && typeof v === "object" && !Array.isArray(v);
22179
+ function kindOf(v) {
22180
+ if (v === null) return "null";
22181
+ if (isArr(v)) return "array";
22182
+ if (isObj(v)) return "object";
22183
+ if (typeof v === "number") return "number";
22184
+ if (typeof v === "boolean") return "boolean";
22185
+ return "string";
22186
+ }
22187
+ function emptyOf(kind) {
22188
+ switch (kind) {
22189
+ case "object":
22190
+ return {};
22191
+ case "array":
22192
+ return [];
22193
+ case "number":
22194
+ return 0;
22195
+ case "boolean":
22196
+ return false;
22197
+ case "null":
22198
+ return null;
22199
+ default:
22200
+ return "";
22201
+ }
22202
+ }
22203
+ function templateFrom(arr) {
22204
+ if (arr.length === 0) return "";
22205
+ const first = arr[0];
22206
+ if (isObj(first)) {
22207
+ const blank = {};
22208
+ for (const k of Object.keys(first)) blank[k] = isObj(first[k]) ? {} : isArr(first[k]) ? [] : first[k];
22209
+ return blank;
22210
+ }
22211
+ if (isArr(first)) return [];
22212
+ return emptyOf(kindOf(first));
22181
22213
  }
22182
- function LeafControl({
22214
+ function ScalarControl({
22183
22215
  value,
22184
22216
  onChange
22185
22217
  }) {
22186
22218
  if (typeof value === "boolean") {
22187
22219
  return /* @__PURE__ */ jsx(Switch, { checked: value, onChange: (c) => onChange(c) });
22188
22220
  }
22189
- if (typeof value === "number") {
22190
- const [draft2, setDraft2] = React74__default.useState(String(value));
22191
- React74__default.useEffect(() => setDraft2(String(value)), [value]);
22192
- const commit2 = () => {
22193
- const n = draft2.trim() === "" ? 0 : Number(draft2);
22194
- onChange(Number.isNaN(n) ? 0 : n);
22195
- };
22196
- return /* @__PURE__ */ jsx(
22197
- Input,
22198
- {
22199
- inputType: "number",
22200
- value: draft2,
22201
- onChange: (e) => setDraft2(e.target.value),
22202
- onBlur: commit2,
22203
- onKeyDown: (e) => {
22204
- if (e.key === "Enter") commit2();
22205
- }
22206
- }
22207
- );
22208
- }
22209
- if (isTraitConfigObject(value)) {
22210
- const [draft2, setDraft2] = React74__default.useState(JSON.stringify(value));
22211
- React74__default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22212
- const commit2 = () => {
22213
- try {
22214
- const parsed = JSON.parse(draft2);
22215
- onChange(parsed);
22216
- } catch {
22217
- }
22218
- };
22219
- return /* @__PURE__ */ jsx(
22220
- Input,
22221
- {
22222
- inputType: "text",
22223
- value: draft2,
22224
- onChange: (e) => setDraft2(e.target.value),
22225
- onBlur: commit2,
22226
- onKeyDown: (e) => {
22227
- if (e.key === "Enter") commit2();
22228
- }
22229
- }
22230
- );
22231
- }
22232
- if (Array.isArray(value)) {
22233
- const [draft2, setDraft2] = React74__default.useState(JSON.stringify(value));
22234
- React74__default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22235
- const commit2 = () => {
22236
- try {
22237
- const parsed = JSON.parse(draft2);
22238
- onChange(parsed);
22239
- } catch {
22240
- }
22241
- };
22242
- return /* @__PURE__ */ jsx(
22243
- Input,
22244
- {
22245
- inputType: "text",
22246
- value: draft2,
22247
- onChange: (e) => setDraft2(e.target.value),
22248
- onBlur: commit2,
22249
- onKeyDown: (e) => {
22250
- if (e.key === "Enter") commit2();
22251
- }
22252
- }
22253
- );
22254
- }
22255
- const strVal = value === null ? "" : String(value);
22256
- const [draft, setDraft] = React74__default.useState(strVal);
22257
- React74__default.useEffect(() => setDraft(value === null ? "" : String(value)), [value]);
22221
+ const numeric = typeof value === "number";
22222
+ const initial = value === null ? "" : String(value);
22223
+ const [draft, setDraft] = React74__default.useState(initial);
22224
+ React74__default.useEffect(() => setDraft(initial), [initial]);
22258
22225
  const commit = () => {
22259
- onChange(draft);
22226
+ if (numeric) {
22227
+ const n = draft.trim() === "" ? 0 : Number(draft);
22228
+ onChange(Number.isNaN(n) ? 0 : n);
22229
+ } else {
22230
+ onChange(draft);
22231
+ }
22260
22232
  };
22261
22233
  return /* @__PURE__ */ jsx(
22262
22234
  Input,
22263
22235
  {
22264
- inputType: "text",
22236
+ inputType: numeric ? "number" : "text",
22265
22237
  value: draft,
22238
+ placeholder: value === null ? "null" : void 0,
22266
22239
  onChange: (e) => setDraft(e.target.value),
22267
22240
  onBlur: commit,
22268
22241
  onKeyDown: (e) => {
@@ -22271,324 +22244,203 @@ function LeafControl({
22271
22244
  }
22272
22245
  );
22273
22246
  }
22274
- function cloneElement(template) {
22275
- if (isTraitConfigObject(template)) {
22276
- const blank = {};
22277
- for (const k of Object.keys(template)) blank[k] = null;
22278
- return blank;
22279
- }
22280
- if (Array.isArray(template)) return [];
22281
- if (typeof template === "boolean") return false;
22282
- if (typeof template === "number") return 0;
22283
- return "";
22284
- }
22285
- var ArrayEditor;
22286
- var init_ArrayEditor = __esm({
22287
- "components/core/molecules/ArrayEditor.tsx"() {
22288
- "use client";
22289
- init_Stack();
22290
- init_Button();
22291
- init_Switch();
22292
- init_Input();
22293
- init_cn();
22294
- ArrayEditor = ({ value, onChange, className }) => {
22295
- const arr = value;
22296
- const update = (idx, next) => {
22297
- const copy = [...arr];
22298
- copy[idx] = next;
22299
- onChange(copy);
22300
- };
22301
- const remove = (idx) => {
22302
- onChange(arr.filter((_, i) => i !== idx));
22303
- };
22304
- const add = () => {
22305
- const template = arr.length > 0 ? arr[0] : "";
22306
- onChange([...arr, cloneElement(template)]);
22307
- };
22308
- return /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("w-full", className), children: [
22309
- arr.map((elem, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
22310
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(LeafControl, { value: elem, onChange: (next) => update(idx, next) }) }),
22311
- /* @__PURE__ */ jsx(
22312
- Button,
22313
- {
22314
- variant: "ghost",
22315
- size: "sm",
22316
- icon: "trash-2",
22317
- onClick: () => remove(idx),
22318
- "aria-label": "Remove item"
22319
- }
22320
- )
22321
- ] }, idx)),
22322
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: "plus", label: "Add item", onClick: add })
22323
- ] });
22324
- };
22325
- }
22326
- });
22327
- function isTraitConfigObject2(v) {
22328
- return v !== null && typeof v === "object" && !Array.isArray(v);
22329
- }
22330
- function ScalarControl({
22331
- fieldKey,
22332
- value,
22333
- onFieldChange
22247
+ function KindSelect({
22248
+ kind,
22249
+ onChange
22334
22250
  }) {
22335
- if (typeof value === "boolean") {
22336
- return /* @__PURE__ */ jsx(Switch, { checked: value, onChange: (c) => onFieldChange(fieldKey, c) });
22337
- }
22338
- if (typeof value === "number") {
22339
- const [draft2, setDraft2] = React74__default.useState(String(value));
22340
- React74__default.useEffect(() => setDraft2(String(value)), [value]);
22341
- const commit2 = () => {
22342
- const n = draft2.trim() === "" ? 0 : Number(draft2);
22343
- onFieldChange(fieldKey, Number.isNaN(n) ? 0 : n);
22344
- };
22345
- return /* @__PURE__ */ jsx(
22346
- Input,
22347
- {
22348
- inputType: "number",
22349
- value: draft2,
22350
- onChange: (e) => setDraft2(e.target.value),
22351
- onBlur: commit2,
22352
- onKeyDown: (e) => {
22353
- if (e.key === "Enter") commit2();
22354
- }
22355
- }
22356
- );
22357
- }
22358
- if (isTraitConfigObject2(value)) {
22359
- const [draft2, setDraft2] = React74__default.useState(JSON.stringify(value));
22360
- React74__default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22361
- const commit2 = () => {
22362
- try {
22363
- onFieldChange(fieldKey, JSON.parse(draft2));
22364
- } catch {
22365
- }
22366
- };
22367
- return /* @__PURE__ */ jsx(
22368
- Input,
22369
- {
22370
- inputType: "text",
22371
- value: draft2,
22372
- onChange: (e) => setDraft2(e.target.value),
22373
- onBlur: commit2,
22374
- onKeyDown: (e) => {
22375
- if (e.key === "Enter") commit2();
22376
- }
22377
- }
22378
- );
22379
- }
22380
- if (Array.isArray(value)) {
22381
- const [draft2, setDraft2] = React74__default.useState(JSON.stringify(value));
22382
- React74__default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22383
- const commit2 = () => {
22384
- try {
22385
- onFieldChange(fieldKey, JSON.parse(draft2));
22386
- } catch {
22387
- }
22388
- };
22389
- return /* @__PURE__ */ jsx(
22390
- Input,
22391
- {
22392
- inputType: "text",
22393
- value: draft2,
22394
- onChange: (e) => setDraft2(e.target.value),
22395
- onBlur: commit2,
22396
- onKeyDown: (e) => {
22397
- if (e.key === "Enter") commit2();
22398
- }
22399
- }
22400
- );
22401
- }
22402
- const [draft, setDraft] = React74__default.useState(value === null ? "" : String(value));
22403
- React74__default.useEffect(() => setDraft(value === null ? "" : String(value)), [value]);
22404
- const commit = () => {
22405
- onFieldChange(fieldKey, draft);
22406
- };
22407
22251
  return /* @__PURE__ */ jsx(
22408
- Input,
22252
+ "select",
22409
22253
  {
22410
- inputType: "text",
22411
- value: draft,
22412
- onChange: (e) => setDraft(e.target.value),
22413
- onBlur: commit,
22414
- onKeyDown: (e) => {
22415
- if (e.key === "Enter") commit();
22416
- }
22254
+ value: kind === "null" ? "string" : kind,
22255
+ onChange: (e) => onChange(e.target.value),
22256
+ "aria-label": "Value type",
22257
+ className: cn(
22258
+ "h-6 rounded-sm bg-muted text-muted-foreground text-[10px] font-mono px-1",
22259
+ "border-[length:var(--border-width-thin)] border-border",
22260
+ "hover:bg-card focus:outline-none focus:ring-1 focus:ring-ring"
22261
+ ),
22262
+ children: KIND_OPTIONS.map((k) => /* @__PURE__ */ jsx("option", { value: k, children: TYPE_LABEL[k] }, k))
22417
22263
  }
22418
22264
  );
22419
22265
  }
22420
- var ObjectEditor;
22421
- var init_ObjectEditor = __esm({
22422
- "components/core/molecules/ObjectEditor.tsx"() {
22423
- "use client";
22424
- init_Stack();
22425
- init_Typography();
22426
- init_Switch();
22427
- init_Input();
22428
- init_cn();
22429
- ObjectEditor = ({ value, onChange, className }) => {
22430
- const entries = Object.entries(value);
22431
- const handleFieldChange = (k, next) => {
22432
- onChange({ ...value, [k]: next });
22433
- };
22434
- if (entries.length === 0) {
22435
- return /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: "Empty object" });
22436
- }
22437
- return /* @__PURE__ */ jsx(VStack, { gap: "xs", className: cn("w-full", className), children: entries.map(([k, v]) => /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "center", children: [
22438
- /* @__PURE__ */ jsx("span", { className: "w-24 shrink-0 truncate", title: k, children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: k }) }),
22439
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(ScalarControl, { fieldKey: k, value: v, onFieldChange: handleFieldChange }) })
22440
- ] }, k)) });
22441
- };
22266
+ function ValueNode({
22267
+ value,
22268
+ onChange,
22269
+ depth
22270
+ }) {
22271
+ const kind = kindOf(value);
22272
+ if (kind === "object" || kind === "array") {
22273
+ return /* @__PURE__ */ jsx(ContainerNode, { value, onChange, depth });
22442
22274
  }
22443
- });
22444
- function isTraitConfigObject3(v) {
22445
- return v !== null && typeof v === "object" && !Array.isArray(v);
22275
+ return /* @__PURE__ */ jsx(ScalarControl, { value, onChange });
22446
22276
  }
22447
- function ValueControl({
22277
+ function Row({
22278
+ rowKey,
22279
+ isArrayItem,
22448
22280
  value,
22449
- onChange
22281
+ depth,
22282
+ onValue,
22283
+ onRenameKey,
22284
+ onChangeKind,
22285
+ onRemove
22450
22286
  }) {
22451
- if (typeof value === "boolean") {
22452
- return /* @__PURE__ */ jsx(Switch, { checked: value, onChange: (c) => onChange(c) });
22453
- }
22454
- if (typeof value === "number") {
22455
- const [draft2, setDraft2] = React74__default.useState(String(value));
22456
- React74__default.useEffect(() => setDraft2(String(value)), [value]);
22457
- const commit2 = () => {
22458
- const n = draft2.trim() === "" ? 0 : Number(draft2);
22459
- onChange(Number.isNaN(n) ? 0 : n);
22460
- };
22461
- return /* @__PURE__ */ jsx(
22462
- Input,
22463
- {
22464
- inputType: "number",
22465
- value: draft2,
22466
- onChange: (e) => setDraft2(e.target.value),
22467
- onBlur: commit2,
22468
- onKeyDown: (e) => {
22469
- if (e.key === "Enter") commit2();
22287
+ const [keyDraft, setKeyDraft] = React74__default.useState(rowKey);
22288
+ React74__default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
22289
+ const container = isObj(value) || isArr(value);
22290
+ return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "group", children: [
22291
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", className: "py-0.5", children: [
22292
+ /* @__PURE__ */ jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22293
+ isArrayItem ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "w-7 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsx("div", { className: "w-28 shrink-0", children: /* @__PURE__ */ jsx(
22294
+ Input,
22295
+ {
22296
+ inputType: "text",
22297
+ value: keyDraft,
22298
+ onChange: (e) => setKeyDraft(e.target.value),
22299
+ onBlur: () => keyDraft !== rowKey && onRenameKey(keyDraft),
22300
+ onKeyDown: (e) => {
22301
+ if (e.key === "Enter" && keyDraft !== rowKey) onRenameKey(keyDraft);
22302
+ },
22303
+ className: "font-mono"
22470
22304
  }
22471
- }
22472
- );
22473
- }
22474
- if (isTraitConfigObject3(value) || Array.isArray(value)) {
22475
- const [draft2, setDraft2] = React74__default.useState(JSON.stringify(value));
22476
- React74__default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22477
- const commit2 = () => {
22478
- try {
22479
- onChange(JSON.parse(draft2));
22480
- } catch {
22481
- }
22482
- };
22483
- return /* @__PURE__ */ jsx(
22484
- Input,
22485
- {
22486
- inputType: "text",
22487
- value: draft2,
22488
- onChange: (e) => setDraft2(e.target.value),
22489
- onBlur: commit2,
22490
- onKeyDown: (e) => {
22491
- if (e.key === "Enter") commit2();
22305
+ ) }),
22306
+ !container && /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22307
+ container && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
22308
+ /* @__PURE__ */ jsx(
22309
+ Button,
22310
+ {
22311
+ variant: "ghost",
22312
+ size: "sm",
22313
+ icon: "x",
22314
+ onClick: onRemove,
22315
+ "aria-label": "Remove",
22316
+ className: "opacity-0 group-hover:opacity-100 transition-opacity"
22492
22317
  }
22493
- }
22494
- );
22495
- }
22496
- const [draft, setDraft] = React74__default.useState(value === null ? "" : String(value));
22497
- React74__default.useEffect(() => setDraft(value === null ? "" : String(value)), [value]);
22498
- const commit = () => {
22499
- onChange(draft);
22500
- };
22501
- return /* @__PURE__ */ jsx(
22502
- Input,
22503
- {
22504
- inputType: "text",
22505
- value: draft,
22506
- onChange: (e) => setDraft(e.target.value),
22507
- onBlur: commit,
22508
- onKeyDown: (e) => {
22509
- if (e.key === "Enter") commit();
22510
- }
22511
- }
22512
- );
22318
+ )
22319
+ ] }),
22320
+ container && /* @__PURE__ */ jsx("div", { className: "pl-3", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22321
+ ] });
22513
22322
  }
22514
- function KeyInput({
22515
- currentKey,
22516
- onRename
22323
+ function ContainerNode({
22324
+ value,
22325
+ onChange,
22326
+ depth
22517
22327
  }) {
22518
- const [draft, setDraft] = React74__default.useState(currentKey);
22519
- React74__default.useEffect(() => setDraft(currentKey), [currentKey]);
22520
- const commit = () => {
22521
- const trimmed = draft.trim();
22522
- if (trimmed !== "" && trimmed !== currentKey) onRename(trimmed);
22523
- else setDraft(currentKey);
22328
+ const [open, setOpen] = React74__default.useState(depth < 2);
22329
+ const array = isArr(value);
22330
+ const entries = array ? value.map((v, i) => [String(i), v]) : Object.entries(value);
22331
+ const setObjValue = (key, next) => {
22332
+ onChange({ ...value, [key]: next });
22524
22333
  };
22525
- return /* @__PURE__ */ jsx(
22526
- Input,
22527
- {
22528
- inputType: "text",
22529
- value: draft,
22530
- onChange: (e) => setDraft(e.target.value),
22531
- onBlur: commit,
22532
- onKeyDown: (e) => {
22533
- if (e.key === "Enter") commit();
22534
- },
22535
- className: "w-24 shrink-0 font-mono text-xs",
22536
- placeholder: "key"
22334
+ const renameKey = (oldKey, newKey) => {
22335
+ if (!newKey || newKey === oldKey) return;
22336
+ const out = {};
22337
+ for (const [k, v] of Object.entries(value)) out[k === oldKey ? newKey : k] = v;
22338
+ onChange(out);
22339
+ };
22340
+ const removeObjKey = (key) => {
22341
+ const out = {};
22342
+ for (const [k, v] of Object.entries(value)) if (k !== key) out[k] = v;
22343
+ onChange(out);
22344
+ };
22345
+ const changeObjKind = (key, k) => setObjValue(key, emptyOf(k));
22346
+ const setArrValue = (idx, next) => {
22347
+ const copy = [...value];
22348
+ copy[idx] = next;
22349
+ onChange(copy);
22350
+ };
22351
+ const removeArrIdx = (idx) => onChange(value.filter((_, i) => i !== idx));
22352
+ const changeArrKind = (idx, k) => setArrValue(idx, emptyOf(k));
22353
+ const add = () => {
22354
+ if (array) {
22355
+ onChange([...value, templateFrom(value)]);
22356
+ } else {
22357
+ const obj = value;
22358
+ let key = "key";
22359
+ let n = 1;
22360
+ while (key in obj) {
22361
+ key = `key${n}`;
22362
+ n += 1;
22363
+ }
22364
+ onChange({ ...obj, [key]: "" });
22537
22365
  }
22538
- );
22539
- }
22540
- var MapEditor;
22541
- var init_MapEditor = __esm({
22542
- "components/core/molecules/MapEditor.tsx"() {
22543
- "use client";
22544
- init_Stack();
22545
- init_Button();
22546
- init_Input();
22547
- init_Switch();
22548
- init_cn();
22549
- MapEditor = ({ value, onChange, className }) => {
22550
- const entries = Object.entries(value);
22551
- const updateKey = (oldKey, newKey) => {
22552
- const next = {};
22553
- for (const [k, v] of Object.entries(value)) {
22554
- next[k === oldKey ? newKey : k] = v;
22555
- }
22556
- onChange(next);
22557
- };
22558
- const updateValue = (k, next) => {
22559
- onChange({ ...value, [k]: next });
22560
- };
22561
- const remove = (k) => {
22562
- const next = { ...value };
22563
- delete next[k];
22564
- onChange(next);
22565
- };
22566
- const add = () => {
22567
- let key = "key";
22568
- let i = 1;
22569
- while (key in value) {
22570
- key = `key${i}`;
22571
- i++;
22572
- }
22573
- onChange({ ...value, [key]: "" });
22574
- };
22575
- return /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: cn("w-full", className), children: [
22576
- entries.map(([k, v]) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
22577
- /* @__PURE__ */ jsx(KeyInput, { currentKey: k, onRename: (newKey) => updateKey(k, newKey) }),
22578
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(ValueControl, { value: v, onChange: (next) => updateValue(k, next) }) }),
22366
+ };
22367
+ const summary = array ? `${entries.length} item${entries.length === 1 ? "" : "s"}` : `${entries.length} field${entries.length === 1 ? "" : "s"}`;
22368
+ return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "w-full", children: [
22369
+ /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", children: /* @__PURE__ */ jsxs(
22370
+ "button",
22371
+ {
22372
+ type: "button",
22373
+ onClick: () => setOpen((o) => !o),
22374
+ className: "flex items-center gap-1 text-muted-foreground hover:text-foreground",
22375
+ "aria-label": open ? "Collapse" : "Expand",
22376
+ children: [
22377
+ /* @__PURE__ */ jsx(Icon, { name: open ? "chevron-down" : "chevron-right", size: "sm" }),
22378
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: array ? "[ ]" : "{ }" }),
22379
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", children: summary })
22380
+ ]
22381
+ }
22382
+ ) }),
22383
+ open && /* @__PURE__ */ jsxs(
22384
+ VStack,
22385
+ {
22386
+ gap: "none",
22387
+ className: cn("ml-2 pl-2 border-l-[length:var(--border-width-thin)] border-border"),
22388
+ children: [
22389
+ entries.map(([k, v], idx) => /* @__PURE__ */ jsx(
22390
+ Row,
22391
+ {
22392
+ rowKey: k,
22393
+ isArrayItem: array,
22394
+ value: v,
22395
+ depth,
22396
+ onValue: (next) => array ? setArrValue(idx, next) : setObjValue(k, next),
22397
+ onRenameKey: (nk) => renameKey(k, nk),
22398
+ onChangeKind: (kind) => array ? changeArrKind(idx, kind) : changeObjKind(k, kind),
22399
+ onRemove: () => array ? removeArrIdx(idx) : removeObjKey(k)
22400
+ },
22401
+ array ? idx : k
22402
+ )),
22579
22403
  /* @__PURE__ */ jsx(
22580
22404
  Button,
22581
22405
  {
22582
22406
  variant: "ghost",
22583
22407
  size: "sm",
22584
- icon: "trash-2",
22585
- onClick: () => remove(k),
22586
- "aria-label": "Remove entry"
22408
+ icon: "plus",
22409
+ label: array ? "Add item" : "Add field",
22410
+ onClick: add,
22411
+ className: "self-start text-muted-foreground"
22587
22412
  }
22588
22413
  )
22589
- ] }, k)),
22590
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", icon: "plus", label: "Add entry", onClick: add })
22591
- ] });
22414
+ ]
22415
+ }
22416
+ )
22417
+ ] });
22418
+ }
22419
+ var isObj, isArr, TYPE_LABEL, KIND_OPTIONS, JsonTreeEditor;
22420
+ var init_JsonTreeEditor = __esm({
22421
+ "components/core/molecules/JsonTreeEditor.tsx"() {
22422
+ "use client";
22423
+ init_Stack();
22424
+ init_Button();
22425
+ init_Input();
22426
+ init_Switch();
22427
+ init_Typography();
22428
+ init_Icon();
22429
+ init_cn();
22430
+ isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
22431
+ isArr = (v) => Array.isArray(v);
22432
+ TYPE_LABEL = {
22433
+ object: "{}",
22434
+ array: "[]",
22435
+ string: "abc",
22436
+ number: "123",
22437
+ boolean: "on/off",
22438
+ null: "\u2014"
22439
+ };
22440
+ KIND_OPTIONS = ["string", "number", "boolean", "object", "array"];
22441
+ JsonTreeEditor = ({ value, onChange, className }) => {
22442
+ const root = value ?? "";
22443
+ return /* @__PURE__ */ jsx("div", { className: cn("w-full rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22592
22444
  };
22593
22445
  }
22594
22446
  });
@@ -35313,7 +35165,7 @@ function TextLikeControl({
35313
35165
  }
35314
35166
  );
35315
35167
  }
35316
- function isTraitConfigObject4(v) {
35168
+ function isTraitConfigObject(v) {
35317
35169
  return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
35318
35170
  }
35319
35171
  function FieldControl({
@@ -35354,30 +35206,10 @@ function FieldControl({
35354
35206
  control = /* @__PURE__ */ jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
35355
35207
  } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
35356
35208
  const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
35357
- control = /* @__PURE__ */ jsx(
35358
- ArrayEditor,
35359
- {
35360
- value: arr,
35361
- onChange: (next) => onChange(name, next)
35362
- }
35363
- );
35364
- } else if (decl.type === "object" && isTraitConfigObject4(effectiveValue)) {
35365
- control = /* @__PURE__ */ jsx(
35366
- ObjectEditor,
35367
- {
35368
- value: effectiveValue,
35369
- onChange: (next) => onChange(name, next)
35370
- }
35371
- );
35372
- } else if (decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject4(effectiveValue)) {
35373
- const obj = isTraitConfigObject4(effectiveValue) ? effectiveValue : {};
35374
- control = /* @__PURE__ */ jsx(
35375
- MapEditor,
35376
- {
35377
- value: obj,
35378
- onChange: (next) => onChange(name, next)
35379
- }
35380
- );
35209
+ control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
35210
+ } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
35211
+ const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
35212
+ control = /* @__PURE__ */ jsx(JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
35381
35213
  } else {
35382
35214
  control = /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "muted", children: [
35383
35215
  decl.type,
@@ -35404,9 +35236,7 @@ var init_PropertyInspector = __esm({
35404
35236
  init_FormSection();
35405
35237
  init_IconPicker();
35406
35238
  init_AssetPicker();
35407
- init_ArrayEditor();
35408
- init_ObjectEditor();
35409
- init_MapEditor();
35239
+ init_JsonTreeEditor();
35410
35240
  TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
35411
35241
  SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
35412
35242
  PropertyInspector = ({
@@ -37302,9 +37132,7 @@ var init_GraphCanvas = __esm({
37302
37132
  var init_molecules2 = __esm({
37303
37133
  "components/core/molecules/index.ts"() {
37304
37134
  init_ErrorBoundary();
37305
- init_ArrayEditor();
37306
- init_ObjectEditor();
37307
- init_MapEditor();
37135
+ init_JsonTreeEditor();
37308
37136
  init_FileTree();
37309
37137
  init_FormField();
37310
37138
  init_EmptyState();
@@ -49933,4 +49761,4 @@ init_AboutPageTemplate();
49933
49761
  // components/index.ts
49934
49762
  init_cn();
49935
49763
 
49936
- export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArrayEditor, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, Coachmark, CodeBlock, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FeatureRenderer2 as FeatureRenderer, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapEditor, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NotifyListener, NumberStepper, ObjectEditor, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };
49764
+ export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, Coachmark, CodeBlock, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FeatureRenderer2 as FeatureRenderer, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NotifyListener, NumberStepper, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };