@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.
@@ -22224,93 +22224,66 @@ var init_DataList = __esm({
22224
22224
  DataList.displayName = "DataList";
22225
22225
  }
22226
22226
  });
22227
- function isTraitConfigObject(v) {
22228
- return v !== null && typeof v === "object" && !Array.isArray(v);
22227
+ function kindOf(v) {
22228
+ if (v === null) return "null";
22229
+ if (isArr(v)) return "array";
22230
+ if (isObj(v)) return "object";
22231
+ if (typeof v === "number") return "number";
22232
+ if (typeof v === "boolean") return "boolean";
22233
+ return "string";
22234
+ }
22235
+ function emptyOf(kind) {
22236
+ switch (kind) {
22237
+ case "object":
22238
+ return {};
22239
+ case "array":
22240
+ return [];
22241
+ case "number":
22242
+ return 0;
22243
+ case "boolean":
22244
+ return false;
22245
+ case "null":
22246
+ return null;
22247
+ default:
22248
+ return "";
22249
+ }
22250
+ }
22251
+ function templateFrom(arr) {
22252
+ if (arr.length === 0) return "";
22253
+ const first = arr[0];
22254
+ if (isObj(first)) {
22255
+ const blank = {};
22256
+ for (const k of Object.keys(first)) blank[k] = isObj(first[k]) ? {} : isArr(first[k]) ? [] : first[k];
22257
+ return blank;
22258
+ }
22259
+ if (isArr(first)) return [];
22260
+ return emptyOf(kindOf(first));
22229
22261
  }
22230
- function LeafControl({
22262
+ function ScalarControl({
22231
22263
  value,
22232
22264
  onChange
22233
22265
  }) {
22234
22266
  if (typeof value === "boolean") {
22235
22267
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Switch, { checked: value, onChange: (c) => onChange(c) });
22236
22268
  }
22237
- if (typeof value === "number") {
22238
- const [draft2, setDraft2] = React74__namespace.default.useState(String(value));
22239
- React74__namespace.default.useEffect(() => setDraft2(String(value)), [value]);
22240
- const commit2 = () => {
22241
- const n = draft2.trim() === "" ? 0 : Number(draft2);
22242
- onChange(Number.isNaN(n) ? 0 : n);
22243
- };
22244
- return /* @__PURE__ */ jsxRuntime.jsx(
22245
- exports.Input,
22246
- {
22247
- inputType: "number",
22248
- value: draft2,
22249
- onChange: (e) => setDraft2(e.target.value),
22250
- onBlur: commit2,
22251
- onKeyDown: (e) => {
22252
- if (e.key === "Enter") commit2();
22253
- }
22254
- }
22255
- );
22256
- }
22257
- if (isTraitConfigObject(value)) {
22258
- const [draft2, setDraft2] = React74__namespace.default.useState(JSON.stringify(value));
22259
- React74__namespace.default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22260
- const commit2 = () => {
22261
- try {
22262
- const parsed = JSON.parse(draft2);
22263
- onChange(parsed);
22264
- } catch {
22265
- }
22266
- };
22267
- return /* @__PURE__ */ jsxRuntime.jsx(
22268
- exports.Input,
22269
- {
22270
- inputType: "text",
22271
- value: draft2,
22272
- onChange: (e) => setDraft2(e.target.value),
22273
- onBlur: commit2,
22274
- onKeyDown: (e) => {
22275
- if (e.key === "Enter") commit2();
22276
- }
22277
- }
22278
- );
22279
- }
22280
- if (Array.isArray(value)) {
22281
- const [draft2, setDraft2] = React74__namespace.default.useState(JSON.stringify(value));
22282
- React74__namespace.default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22283
- const commit2 = () => {
22284
- try {
22285
- const parsed = JSON.parse(draft2);
22286
- onChange(parsed);
22287
- } catch {
22288
- }
22289
- };
22290
- return /* @__PURE__ */ jsxRuntime.jsx(
22291
- exports.Input,
22292
- {
22293
- inputType: "text",
22294
- value: draft2,
22295
- onChange: (e) => setDraft2(e.target.value),
22296
- onBlur: commit2,
22297
- onKeyDown: (e) => {
22298
- if (e.key === "Enter") commit2();
22299
- }
22300
- }
22301
- );
22302
- }
22303
- const strVal = value === null ? "" : String(value);
22304
- const [draft, setDraft] = React74__namespace.default.useState(strVal);
22305
- React74__namespace.default.useEffect(() => setDraft(value === null ? "" : String(value)), [value]);
22269
+ const numeric = typeof value === "number";
22270
+ const initial = value === null ? "" : String(value);
22271
+ const [draft, setDraft] = React74__namespace.default.useState(initial);
22272
+ React74__namespace.default.useEffect(() => setDraft(initial), [initial]);
22306
22273
  const commit = () => {
22307
- onChange(draft);
22274
+ if (numeric) {
22275
+ const n = draft.trim() === "" ? 0 : Number(draft);
22276
+ onChange(Number.isNaN(n) ? 0 : n);
22277
+ } else {
22278
+ onChange(draft);
22279
+ }
22308
22280
  };
22309
22281
  return /* @__PURE__ */ jsxRuntime.jsx(
22310
22282
  exports.Input,
22311
22283
  {
22312
- inputType: "text",
22284
+ inputType: numeric ? "number" : "text",
22313
22285
  value: draft,
22286
+ placeholder: value === null ? "null" : void 0,
22314
22287
  onChange: (e) => setDraft(e.target.value),
22315
22288
  onBlur: commit,
22316
22289
  onKeyDown: (e) => {
@@ -22319,324 +22292,203 @@ function LeafControl({
22319
22292
  }
22320
22293
  );
22321
22294
  }
22322
- function cloneElement(template) {
22323
- if (isTraitConfigObject(template)) {
22324
- const blank = {};
22325
- for (const k of Object.keys(template)) blank[k] = null;
22326
- return blank;
22327
- }
22328
- if (Array.isArray(template)) return [];
22329
- if (typeof template === "boolean") return false;
22330
- if (typeof template === "number") return 0;
22331
- return "";
22332
- }
22333
- exports.ArrayEditor = void 0;
22334
- var init_ArrayEditor = __esm({
22335
- "components/core/molecules/ArrayEditor.tsx"() {
22336
- "use client";
22337
- init_Stack();
22338
- init_Button();
22339
- init_Switch();
22340
- init_Input();
22341
- init_cn();
22342
- exports.ArrayEditor = ({ value, onChange, className }) => {
22343
- const arr = value;
22344
- const update = (idx, next) => {
22345
- const copy = [...arr];
22346
- copy[idx] = next;
22347
- onChange(copy);
22348
- };
22349
- const remove = (idx) => {
22350
- onChange(arr.filter((_, i) => i !== idx));
22351
- };
22352
- const add = () => {
22353
- const template = arr.length > 0 ? arr[0] : "";
22354
- onChange([...arr, cloneElement(template)]);
22355
- };
22356
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: cn("w-full", className), children: [
22357
- arr.map((elem, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
22358
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(LeafControl, { value: elem, onChange: (next) => update(idx, next) }) }),
22359
- /* @__PURE__ */ jsxRuntime.jsx(
22360
- exports.Button,
22361
- {
22362
- variant: "ghost",
22363
- size: "sm",
22364
- icon: "trash-2",
22365
- onClick: () => remove(idx),
22366
- "aria-label": "Remove item"
22367
- }
22368
- )
22369
- ] }, idx)),
22370
- /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", icon: "plus", label: "Add item", onClick: add })
22371
- ] });
22372
- };
22373
- }
22374
- });
22375
- function isTraitConfigObject2(v) {
22376
- return v !== null && typeof v === "object" && !Array.isArray(v);
22377
- }
22378
- function ScalarControl({
22379
- fieldKey,
22380
- value,
22381
- onFieldChange
22295
+ function KindSelect({
22296
+ kind,
22297
+ onChange
22382
22298
  }) {
22383
- if (typeof value === "boolean") {
22384
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Switch, { checked: value, onChange: (c) => onFieldChange(fieldKey, c) });
22385
- }
22386
- if (typeof value === "number") {
22387
- const [draft2, setDraft2] = React74__namespace.default.useState(String(value));
22388
- React74__namespace.default.useEffect(() => setDraft2(String(value)), [value]);
22389
- const commit2 = () => {
22390
- const n = draft2.trim() === "" ? 0 : Number(draft2);
22391
- onFieldChange(fieldKey, Number.isNaN(n) ? 0 : n);
22392
- };
22393
- return /* @__PURE__ */ jsxRuntime.jsx(
22394
- exports.Input,
22395
- {
22396
- inputType: "number",
22397
- value: draft2,
22398
- onChange: (e) => setDraft2(e.target.value),
22399
- onBlur: commit2,
22400
- onKeyDown: (e) => {
22401
- if (e.key === "Enter") commit2();
22402
- }
22403
- }
22404
- );
22405
- }
22406
- if (isTraitConfigObject2(value)) {
22407
- const [draft2, setDraft2] = React74__namespace.default.useState(JSON.stringify(value));
22408
- React74__namespace.default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22409
- const commit2 = () => {
22410
- try {
22411
- onFieldChange(fieldKey, JSON.parse(draft2));
22412
- } catch {
22413
- }
22414
- };
22415
- return /* @__PURE__ */ jsxRuntime.jsx(
22416
- exports.Input,
22417
- {
22418
- inputType: "text",
22419
- value: draft2,
22420
- onChange: (e) => setDraft2(e.target.value),
22421
- onBlur: commit2,
22422
- onKeyDown: (e) => {
22423
- if (e.key === "Enter") commit2();
22424
- }
22425
- }
22426
- );
22427
- }
22428
- if (Array.isArray(value)) {
22429
- const [draft2, setDraft2] = React74__namespace.default.useState(JSON.stringify(value));
22430
- React74__namespace.default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22431
- const commit2 = () => {
22432
- try {
22433
- onFieldChange(fieldKey, JSON.parse(draft2));
22434
- } catch {
22435
- }
22436
- };
22437
- return /* @__PURE__ */ jsxRuntime.jsx(
22438
- exports.Input,
22439
- {
22440
- inputType: "text",
22441
- value: draft2,
22442
- onChange: (e) => setDraft2(e.target.value),
22443
- onBlur: commit2,
22444
- onKeyDown: (e) => {
22445
- if (e.key === "Enter") commit2();
22446
- }
22447
- }
22448
- );
22449
- }
22450
- const [draft, setDraft] = React74__namespace.default.useState(value === null ? "" : String(value));
22451
- React74__namespace.default.useEffect(() => setDraft(value === null ? "" : String(value)), [value]);
22452
- const commit = () => {
22453
- onFieldChange(fieldKey, draft);
22454
- };
22455
22299
  return /* @__PURE__ */ jsxRuntime.jsx(
22456
- exports.Input,
22300
+ "select",
22457
22301
  {
22458
- inputType: "text",
22459
- value: draft,
22460
- onChange: (e) => setDraft(e.target.value),
22461
- onBlur: commit,
22462
- onKeyDown: (e) => {
22463
- if (e.key === "Enter") commit();
22464
- }
22302
+ value: kind === "null" ? "string" : kind,
22303
+ onChange: (e) => onChange(e.target.value),
22304
+ "aria-label": "Value type",
22305
+ className: cn(
22306
+ "h-6 rounded-sm bg-muted text-muted-foreground text-[10px] font-mono px-1",
22307
+ "border-[length:var(--border-width-thin)] border-border",
22308
+ "hover:bg-card focus:outline-none focus:ring-1 focus:ring-ring"
22309
+ ),
22310
+ children: KIND_OPTIONS.map((k) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: k, children: TYPE_LABEL[k] }, k))
22465
22311
  }
22466
22312
  );
22467
22313
  }
22468
- exports.ObjectEditor = void 0;
22469
- var init_ObjectEditor = __esm({
22470
- "components/core/molecules/ObjectEditor.tsx"() {
22471
- "use client";
22472
- init_Stack();
22473
- init_Typography();
22474
- init_Switch();
22475
- init_Input();
22476
- init_cn();
22477
- exports.ObjectEditor = ({ value, onChange, className }) => {
22478
- const entries = Object.entries(value);
22479
- const handleFieldChange = (k, next) => {
22480
- onChange({ ...value, [k]: next });
22481
- };
22482
- if (entries.length === 0) {
22483
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: "Empty object" });
22484
- }
22485
- return /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "xs", className: cn("w-full", className), children: entries.map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", align: "center", children: [
22486
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-24 shrink-0 truncate", title: k, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: k }) }),
22487
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(ScalarControl, { fieldKey: k, value: v, onFieldChange: handleFieldChange }) })
22488
- ] }, k)) });
22489
- };
22314
+ function ValueNode({
22315
+ value,
22316
+ onChange,
22317
+ depth
22318
+ }) {
22319
+ const kind = kindOf(value);
22320
+ if (kind === "object" || kind === "array") {
22321
+ return /* @__PURE__ */ jsxRuntime.jsx(ContainerNode, { value, onChange, depth });
22490
22322
  }
22491
- });
22492
- function isTraitConfigObject3(v) {
22493
- return v !== null && typeof v === "object" && !Array.isArray(v);
22323
+ return /* @__PURE__ */ jsxRuntime.jsx(ScalarControl, { value, onChange });
22494
22324
  }
22495
- function ValueControl({
22325
+ function Row({
22326
+ rowKey,
22327
+ isArrayItem,
22496
22328
  value,
22497
- onChange
22329
+ depth,
22330
+ onValue,
22331
+ onRenameKey,
22332
+ onChangeKind,
22333
+ onRemove
22498
22334
  }) {
22499
- if (typeof value === "boolean") {
22500
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Switch, { checked: value, onChange: (c) => onChange(c) });
22501
- }
22502
- if (typeof value === "number") {
22503
- const [draft2, setDraft2] = React74__namespace.default.useState(String(value));
22504
- React74__namespace.default.useEffect(() => setDraft2(String(value)), [value]);
22505
- const commit2 = () => {
22506
- const n = draft2.trim() === "" ? 0 : Number(draft2);
22507
- onChange(Number.isNaN(n) ? 0 : n);
22508
- };
22509
- return /* @__PURE__ */ jsxRuntime.jsx(
22510
- exports.Input,
22511
- {
22512
- inputType: "number",
22513
- value: draft2,
22514
- onChange: (e) => setDraft2(e.target.value),
22515
- onBlur: commit2,
22516
- onKeyDown: (e) => {
22517
- if (e.key === "Enter") commit2();
22335
+ const [keyDraft, setKeyDraft] = React74__namespace.default.useState(rowKey);
22336
+ React74__namespace.default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
22337
+ const container = isObj(value) || isArr(value);
22338
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "group", children: [
22339
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", className: "py-0.5", children: [
22340
+ /* @__PURE__ */ jsxRuntime.jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22341
+ isArrayItem ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", className: "w-7 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-28 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
22342
+ exports.Input,
22343
+ {
22344
+ inputType: "text",
22345
+ value: keyDraft,
22346
+ onChange: (e) => setKeyDraft(e.target.value),
22347
+ onBlur: () => keyDraft !== rowKey && onRenameKey(keyDraft),
22348
+ onKeyDown: (e) => {
22349
+ if (e.key === "Enter" && keyDraft !== rowKey) onRenameKey(keyDraft);
22350
+ },
22351
+ className: "font-mono"
22518
22352
  }
22519
- }
22520
- );
22521
- }
22522
- if (isTraitConfigObject3(value) || Array.isArray(value)) {
22523
- const [draft2, setDraft2] = React74__namespace.default.useState(JSON.stringify(value));
22524
- React74__namespace.default.useEffect(() => setDraft2(JSON.stringify(value)), [value]);
22525
- const commit2 = () => {
22526
- try {
22527
- onChange(JSON.parse(draft2));
22528
- } catch {
22529
- }
22530
- };
22531
- return /* @__PURE__ */ jsxRuntime.jsx(
22532
- exports.Input,
22533
- {
22534
- inputType: "text",
22535
- value: draft2,
22536
- onChange: (e) => setDraft2(e.target.value),
22537
- onBlur: commit2,
22538
- onKeyDown: (e) => {
22539
- if (e.key === "Enter") commit2();
22353
+ ) }),
22354
+ !container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22355
+ container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
22356
+ /* @__PURE__ */ jsxRuntime.jsx(
22357
+ exports.Button,
22358
+ {
22359
+ variant: "ghost",
22360
+ size: "sm",
22361
+ icon: "x",
22362
+ onClick: onRemove,
22363
+ "aria-label": "Remove",
22364
+ className: "opacity-0 group-hover:opacity-100 transition-opacity"
22540
22365
  }
22541
- }
22542
- );
22543
- }
22544
- const [draft, setDraft] = React74__namespace.default.useState(value === null ? "" : String(value));
22545
- React74__namespace.default.useEffect(() => setDraft(value === null ? "" : String(value)), [value]);
22546
- const commit = () => {
22547
- onChange(draft);
22548
- };
22549
- return /* @__PURE__ */ jsxRuntime.jsx(
22550
- exports.Input,
22551
- {
22552
- inputType: "text",
22553
- value: draft,
22554
- onChange: (e) => setDraft(e.target.value),
22555
- onBlur: commit,
22556
- onKeyDown: (e) => {
22557
- if (e.key === "Enter") commit();
22558
- }
22559
- }
22560
- );
22366
+ )
22367
+ ] }),
22368
+ container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-3", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22369
+ ] });
22561
22370
  }
22562
- function KeyInput({
22563
- currentKey,
22564
- onRename
22371
+ function ContainerNode({
22372
+ value,
22373
+ onChange,
22374
+ depth
22565
22375
  }) {
22566
- const [draft, setDraft] = React74__namespace.default.useState(currentKey);
22567
- React74__namespace.default.useEffect(() => setDraft(currentKey), [currentKey]);
22568
- const commit = () => {
22569
- const trimmed = draft.trim();
22570
- if (trimmed !== "" && trimmed !== currentKey) onRename(trimmed);
22571
- else setDraft(currentKey);
22376
+ const [open, setOpen] = React74__namespace.default.useState(depth < 2);
22377
+ const array = isArr(value);
22378
+ const entries = array ? value.map((v, i) => [String(i), v]) : Object.entries(value);
22379
+ const setObjValue = (key, next) => {
22380
+ onChange({ ...value, [key]: next });
22572
22381
  };
22573
- return /* @__PURE__ */ jsxRuntime.jsx(
22574
- exports.Input,
22575
- {
22576
- inputType: "text",
22577
- value: draft,
22578
- onChange: (e) => setDraft(e.target.value),
22579
- onBlur: commit,
22580
- onKeyDown: (e) => {
22581
- if (e.key === "Enter") commit();
22582
- },
22583
- className: "w-24 shrink-0 font-mono text-xs",
22584
- placeholder: "key"
22382
+ const renameKey = (oldKey, newKey) => {
22383
+ if (!newKey || newKey === oldKey) return;
22384
+ const out = {};
22385
+ for (const [k, v] of Object.entries(value)) out[k === oldKey ? newKey : k] = v;
22386
+ onChange(out);
22387
+ };
22388
+ const removeObjKey = (key) => {
22389
+ const out = {};
22390
+ for (const [k, v] of Object.entries(value)) if (k !== key) out[k] = v;
22391
+ onChange(out);
22392
+ };
22393
+ const changeObjKind = (key, k) => setObjValue(key, emptyOf(k));
22394
+ const setArrValue = (idx, next) => {
22395
+ const copy = [...value];
22396
+ copy[idx] = next;
22397
+ onChange(copy);
22398
+ };
22399
+ const removeArrIdx = (idx) => onChange(value.filter((_, i) => i !== idx));
22400
+ const changeArrKind = (idx, k) => setArrValue(idx, emptyOf(k));
22401
+ const add = () => {
22402
+ if (array) {
22403
+ onChange([...value, templateFrom(value)]);
22404
+ } else {
22405
+ const obj = value;
22406
+ let key = "key";
22407
+ let n = 1;
22408
+ while (key in obj) {
22409
+ key = `key${n}`;
22410
+ n += 1;
22411
+ }
22412
+ onChange({ ...obj, [key]: "" });
22585
22413
  }
22586
- );
22587
- }
22588
- exports.MapEditor = void 0;
22589
- var init_MapEditor = __esm({
22590
- "components/core/molecules/MapEditor.tsx"() {
22591
- "use client";
22592
- init_Stack();
22593
- init_Button();
22594
- init_Input();
22595
- init_Switch();
22596
- init_cn();
22597
- exports.MapEditor = ({ value, onChange, className }) => {
22598
- const entries = Object.entries(value);
22599
- const updateKey = (oldKey, newKey) => {
22600
- const next = {};
22601
- for (const [k, v] of Object.entries(value)) {
22602
- next[k === oldKey ? newKey : k] = v;
22603
- }
22604
- onChange(next);
22605
- };
22606
- const updateValue = (k, next) => {
22607
- onChange({ ...value, [k]: next });
22608
- };
22609
- const remove = (k) => {
22610
- const next = { ...value };
22611
- delete next[k];
22612
- onChange(next);
22613
- };
22614
- const add = () => {
22615
- let key = "key";
22616
- let i = 1;
22617
- while (key in value) {
22618
- key = `key${i}`;
22619
- i++;
22620
- }
22621
- onChange({ ...value, [key]: "" });
22622
- };
22623
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: cn("w-full", className), children: [
22624
- entries.map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
22625
- /* @__PURE__ */ jsxRuntime.jsx(KeyInput, { currentKey: k, onRename: (newKey) => updateKey(k, newKey) }),
22626
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(ValueControl, { value: v, onChange: (next) => updateValue(k, next) }) }),
22414
+ };
22415
+ const summary = array ? `${entries.length} item${entries.length === 1 ? "" : "s"}` : `${entries.length} field${entries.length === 1 ? "" : "s"}`;
22416
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "w-full", children: [
22417
+ /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", align: "center", children: /* @__PURE__ */ jsxRuntime.jsxs(
22418
+ "button",
22419
+ {
22420
+ type: "button",
22421
+ onClick: () => setOpen((o) => !o),
22422
+ className: "flex items-center gap-1 text-muted-foreground hover:text-foreground",
22423
+ "aria-label": open ? "Collapse" : "Expand",
22424
+ children: [
22425
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: open ? "chevron-down" : "chevron-right", size: "sm" }),
22426
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-xs", children: array ? "[ ]" : "{ }" }),
22427
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", children: summary })
22428
+ ]
22429
+ }
22430
+ ) }),
22431
+ open && /* @__PURE__ */ jsxRuntime.jsxs(
22432
+ exports.VStack,
22433
+ {
22434
+ gap: "none",
22435
+ className: cn("ml-2 pl-2 border-l-[length:var(--border-width-thin)] border-border"),
22436
+ children: [
22437
+ entries.map(([k, v], idx) => /* @__PURE__ */ jsxRuntime.jsx(
22438
+ Row,
22439
+ {
22440
+ rowKey: k,
22441
+ isArrayItem: array,
22442
+ value: v,
22443
+ depth,
22444
+ onValue: (next) => array ? setArrValue(idx, next) : setObjValue(k, next),
22445
+ onRenameKey: (nk) => renameKey(k, nk),
22446
+ onChangeKind: (kind) => array ? changeArrKind(idx, kind) : changeObjKind(k, kind),
22447
+ onRemove: () => array ? removeArrIdx(idx) : removeObjKey(k)
22448
+ },
22449
+ array ? idx : k
22450
+ )),
22627
22451
  /* @__PURE__ */ jsxRuntime.jsx(
22628
22452
  exports.Button,
22629
22453
  {
22630
22454
  variant: "ghost",
22631
22455
  size: "sm",
22632
- icon: "trash-2",
22633
- onClick: () => remove(k),
22634
- "aria-label": "Remove entry"
22456
+ icon: "plus",
22457
+ label: array ? "Add item" : "Add field",
22458
+ onClick: add,
22459
+ className: "self-start text-muted-foreground"
22635
22460
  }
22636
22461
  )
22637
- ] }, k)),
22638
- /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", icon: "plus", label: "Add entry", onClick: add })
22639
- ] });
22462
+ ]
22463
+ }
22464
+ )
22465
+ ] });
22466
+ }
22467
+ var isObj, isArr, TYPE_LABEL, KIND_OPTIONS; exports.JsonTreeEditor = void 0;
22468
+ var init_JsonTreeEditor = __esm({
22469
+ "components/core/molecules/JsonTreeEditor.tsx"() {
22470
+ "use client";
22471
+ init_Stack();
22472
+ init_Button();
22473
+ init_Input();
22474
+ init_Switch();
22475
+ init_Typography();
22476
+ init_Icon();
22477
+ init_cn();
22478
+ isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
22479
+ isArr = (v) => Array.isArray(v);
22480
+ TYPE_LABEL = {
22481
+ object: "{}",
22482
+ array: "[]",
22483
+ string: "abc",
22484
+ number: "123",
22485
+ boolean: "on/off",
22486
+ null: "\u2014"
22487
+ };
22488
+ KIND_OPTIONS = ["string", "number", "boolean", "object", "array"];
22489
+ exports.JsonTreeEditor = ({ value, onChange, className }) => {
22490
+ const root = value ?? "";
22491
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22640
22492
  };
22641
22493
  }
22642
22494
  });
@@ -35361,7 +35213,7 @@ function TextLikeControl({
35361
35213
  }
35362
35214
  );
35363
35215
  }
35364
- function isTraitConfigObject4(v) {
35216
+ function isTraitConfigObject(v) {
35365
35217
  return v !== null && v !== void 0 && typeof v === "object" && !Array.isArray(v);
35366
35218
  }
35367
35219
  function FieldControl({
@@ -35402,30 +35254,10 @@ function FieldControl({
35402
35254
  control = /* @__PURE__ */ jsxRuntime.jsx(TextLikeControl, { field: name, numeric: false, value, onCommit: onChange });
35403
35255
  } else if (decl.type.startsWith("[") || Array.isArray(effectiveValue)) {
35404
35256
  const arr = Array.isArray(effectiveValue) ? effectiveValue : [];
35405
- control = /* @__PURE__ */ jsxRuntime.jsx(
35406
- exports.ArrayEditor,
35407
- {
35408
- value: arr,
35409
- onChange: (next) => onChange(name, next)
35410
- }
35411
- );
35412
- } else if (decl.type === "object" && isTraitConfigObject4(effectiveValue)) {
35413
- control = /* @__PURE__ */ jsxRuntime.jsx(
35414
- exports.ObjectEditor,
35415
- {
35416
- value: effectiveValue,
35417
- onChange: (next) => onChange(name, next)
35418
- }
35419
- );
35420
- } else if (decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject4(effectiveValue)) {
35421
- const obj = isTraitConfigObject4(effectiveValue) ? effectiveValue : {};
35422
- control = /* @__PURE__ */ jsxRuntime.jsx(
35423
- exports.MapEditor,
35424
- {
35425
- value: obj,
35426
- onChange: (next) => onChange(name, next)
35427
- }
35428
- );
35257
+ control = /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: arr, onChange: (next) => onChange(name, next) });
35258
+ } else if (decl.type === "object" || decl.type.startsWith("Map ") || !SCALAR_TYPES.has(decl.type) && isTraitConfigObject(effectiveValue)) {
35259
+ const obj = isTraitConfigObject(effectiveValue) ? effectiveValue : {};
35260
+ control = /* @__PURE__ */ jsxRuntime.jsx(exports.JsonTreeEditor, { value: obj, onChange: (next) => onChange(name, next) });
35429
35261
  } else {
35430
35262
  control = /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", color: "muted", children: [
35431
35263
  decl.type,
@@ -35452,9 +35284,7 @@ var init_PropertyInspector = __esm({
35452
35284
  init_FormSection();
35453
35285
  init_IconPicker();
35454
35286
  init_AssetPicker();
35455
- init_ArrayEditor();
35456
- init_ObjectEditor();
35457
- init_MapEditor();
35287
+ init_JsonTreeEditor();
35458
35288
  TIER_ORDER = ["presentation", "domain", "policy", "infra", "internal"];
35459
35289
  SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "icon", "asset"]);
35460
35290
  exports.PropertyInspector = ({
@@ -37350,9 +37180,7 @@ var init_GraphCanvas = __esm({
37350
37180
  var init_molecules2 = __esm({
37351
37181
  "components/core/molecules/index.ts"() {
37352
37182
  init_ErrorBoundary();
37353
- init_ArrayEditor();
37354
- init_ObjectEditor();
37355
- init_MapEditor();
37183
+ init_JsonTreeEditor();
37356
37184
  init_FileTree();
37357
37185
  init_FormField();
37358
37186
  init_EmptyState();