@maykonpaulo/maestro-admin 0.3.0 → 0.4.0-next.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/components/MaestroAdmin.tsx
2
- import { useMemo as useMemo3, useState as useState10 } from "react";
2
+ import { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMemo7, useState as useState14 } from "react";
3
3
 
4
4
  // src/client/MaestroClient.ts
5
5
  var MaestroApiError = class extends Error {
@@ -110,6 +110,11 @@ function useEntity(entityId) {
110
110
  const entities = useEntities();
111
111
  return entityId ? entities.find((e) => e.id === entityId) : void 0;
112
112
  }
113
+ var RootContext = createContext(null);
114
+ var AdminRootProvider = RootContext.Provider;
115
+ function useAdminRoot() {
116
+ return useContext(RootContext);
117
+ }
113
118
 
114
119
  // src/hooks.ts
115
120
  import { useCallback, useEffect, useState } from "react";
@@ -198,9 +203,6 @@ function formFields(entity, mode) {
198
203
  return entity.fields.filter((f) => mode === "create" ? f.form.creatable : f.form.editable).sort((a, b) => (a.form.order ?? 0) - (b.form.order ?? 0));
199
204
  }
200
205
 
201
- // src/components/Sidebar.tsx
202
- import { useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
203
-
204
206
  // src/grouping.ts
205
207
  var MIN_PREFIX = 4;
206
208
  function commonPrefix(a, b) {
@@ -257,6 +259,9 @@ function buildMenu(entities, labels) {
257
259
  return { groups, ungrouped };
258
260
  }
259
261
 
262
+ // src/components/Sidebar.tsx
263
+ import { useMemo, useState as useState2 } from "react";
264
+
260
265
  // src/i18n.ts
261
266
  import { createContext as createContext2, useContext as useContext2 } from "react";
262
267
  var EN = {
@@ -264,7 +269,7 @@ var EN = {
264
269
  loadingMetadata: "Loading metadata\u2026",
265
270
  records: (total) => `${total} record${total === 1 ? "" : "s"}`,
266
271
  searchPlaceholder: (plural) => `Search ${plural.toLowerCase()}\u2026`,
267
- newRecord: "+ New",
272
+ newRecord: "New",
268
273
  view: "View",
269
274
  edit: "Edit",
270
275
  clone: "Clone",
@@ -303,14 +308,42 @@ var EN = {
303
308
  searchMenu: "Search menu\u2026",
304
309
  recents: "Recents",
305
310
  menus: "Menus",
306
- noMenuResults: "No matches"
311
+ noMenuResults: "No matches",
312
+ hub: "Hub",
313
+ breadcrumb: "Breadcrumb",
314
+ search: "Search",
315
+ commandKey: "\u2318K",
316
+ commandPalette: "Command palette",
317
+ palettePlaceholder: "Go to an entity, or run an action\u2026",
318
+ goTo: "Go to",
319
+ onThisScreen: "On this screen",
320
+ appearance: "Appearance",
321
+ palette: "Palette",
322
+ density: "Density",
323
+ colorScheme: "Light / dark",
324
+ schemeSystem: "system",
325
+ schemeLight: "light",
326
+ schemeDark: "dark",
327
+ entities: "Entities",
328
+ hubMeta: (entities, sources) => `${entities} ${entities === 1 ? "entity" : "entities"} across ${sources} ${sources === 1 ? "datasource" : "datasources"}.`,
329
+ otherEntities: "Other",
330
+ noEntities: "No entities",
331
+ noEntitiesHint: "The server served an empty metadata document.",
332
+ fieldCount: (count) => `${count} field${count === 1 ? "" : "s"}`,
333
+ capRead: "read",
334
+ capCreate: "create",
335
+ capUpdate: "update",
336
+ capDelete: "delete",
337
+ columns: "Columns",
338
+ columnsCount: (visible, total) => `${visible}/${total}`,
339
+ resetColumns: "Reset columns"
307
340
  };
308
341
  var PT_BR = {
309
342
  loading: "Carregando\u2026",
310
343
  loadingMetadata: "Carregando metadados\u2026",
311
344
  records: (total) => `${total} registro${total === 1 ? "" : "s"}`,
312
345
  searchPlaceholder: (plural) => `Buscar ${plural.toLowerCase()}\u2026`,
313
- newRecord: "+ Novo",
346
+ newRecord: "Novo",
314
347
  view: "Visualizar",
315
348
  edit: "Editar",
316
349
  clone: "Clonar",
@@ -349,7 +382,35 @@ var PT_BR = {
349
382
  searchMenu: "Buscar no menu\u2026",
350
383
  recents: "Recentes",
351
384
  menus: "Menus",
352
- noMenuResults: "Nada encontrado"
385
+ noMenuResults: "Nada encontrado",
386
+ hub: "Hub",
387
+ breadcrumb: "Trilha",
388
+ search: "Buscar",
389
+ commandKey: "\u2318K",
390
+ commandPalette: "Paleta de comandos",
391
+ palettePlaceholder: "Ir para uma entidade, ou executar uma a\xE7\xE3o\u2026",
392
+ goTo: "Ir para",
393
+ onThisScreen: "Nesta tela",
394
+ appearance: "Apar\xEAncia",
395
+ palette: "Paleta",
396
+ density: "Densidade",
397
+ colorScheme: "Claro / escuro",
398
+ schemeSystem: "sistema",
399
+ schemeLight: "claro",
400
+ schemeDark: "escuro",
401
+ entities: "Entidades",
402
+ hubMeta: (entities, sources) => `${entities} ${entities === 1 ? "entidade" : "entidades"} em ${sources} ${sources === 1 ? "origem de dados" : "origens de dados"}.`,
403
+ otherEntities: "Outras",
404
+ noEntities: "Nenhuma entidade",
405
+ noEntitiesHint: "O servidor entregou um documento de metadados vazio.",
406
+ fieldCount: (count) => `${count} campo${count === 1 ? "" : "s"}`,
407
+ capRead: "ler",
408
+ capCreate: "criar",
409
+ capUpdate: "editar",
410
+ capDelete: "excluir",
411
+ columns: "Colunas",
412
+ columnsCount: (visible, total) => `${visible}/${total}`,
413
+ resetColumns: "Restaurar colunas"
353
414
  };
354
415
  var LOCALES = { en: EN, "pt-BR": PT_BR };
355
416
  function stringsFor(locale) {
@@ -361,8 +422,251 @@ function useT() {
361
422
  return useContext2(I18nContext);
362
423
  }
363
424
 
364
- // src/components/Sidebar.tsx
425
+ // src/components/icons.tsx
365
426
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
427
+ var PATHS = {
428
+ hub: /* @__PURE__ */ jsxs(Fragment, { children: [
429
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.5" }),
430
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "3.5", width: "7", height: "7", rx: "1.5" }),
431
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "13.5", width: "7", height: "7", rx: "1.5" }),
432
+ /* @__PURE__ */ jsx("rect", { x: "13.5", y: "13.5", width: "7", height: "7", rx: "1.5" })
433
+ ] }),
434
+ search: /* @__PURE__ */ jsxs(Fragment, { children: [
435
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "6.5" }),
436
+ /* @__PURE__ */ jsx("path", { d: "m16 16 4.5 4.5" })
437
+ ] }),
438
+ chevronRight: /* @__PURE__ */ jsx("path", { d: "m9 5 7 7-7 7" }),
439
+ chevronLeft: /* @__PURE__ */ jsx("path", { d: "M15 5 8 12l7 7" }),
440
+ chevronsLeft: /* @__PURE__ */ jsx("path", { d: "M17 5l-7 7 7 7M10 5l-7 7 7 7" }),
441
+ chevronsRight: /* @__PURE__ */ jsx("path", { d: "m7 5 7 7-7 7M14 5l7 7-7 7" }),
442
+ plus: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }),
443
+ close: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6 6 18" }),
444
+ check: /* @__PURE__ */ jsx("path", { d: "m4.5 12.5 5 5 10-11" }),
445
+ filter: /* @__PURE__ */ jsx("path", { d: "M3 5h18l-7 8v6l-4 2v-8L3 5Z" }),
446
+ download: /* @__PURE__ */ jsx("path", { d: "M12 3v12M7.5 10.5 12 15l4.5-4.5M4 20h16" }),
447
+ columns: /* @__PURE__ */ jsxs(Fragment, { children: [
448
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
449
+ /* @__PURE__ */ jsx("path", { d: "M9.5 4v16M15 4v16" })
450
+ ] }),
451
+ rows: /* @__PURE__ */ jsxs(Fragment, { children: [
452
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
453
+ /* @__PURE__ */ jsx("path", { d: "M3 9.3h18M3 14.7h18" })
454
+ ] }),
455
+ sun: /* @__PURE__ */ jsxs(Fragment, { children: [
456
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "4.2" }),
457
+ /* @__PURE__ */ jsx("path", { d: "M12 2.5v2.2M12 19.3v2.2M4.2 4.2l1.6 1.6M18.2 18.2l1.6 1.6M2.5 12h2.2M19.3 12h2.2M4.2 19.8l1.6-1.6M18.2 5.8l1.6-1.6" })
458
+ ] }),
459
+ moon: /* @__PURE__ */ jsx("path", { d: "M20 14.5A8.5 8.5 0 0 1 9.5 4a8.5 8.5 0 1 0 10.5 10.5Z" }),
460
+ swatch: /* @__PURE__ */ jsxs(Fragment, { children: [
461
+ /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "4.5" }),
462
+ /* @__PURE__ */ jsx("circle", { cx: "15.5", cy: "8.5", r: "4.5" }),
463
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "15", r: "4.5" })
464
+ ] }),
465
+ dots: /* @__PURE__ */ jsxs(Fragment, { children: [
466
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.4" }),
467
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.4" }),
468
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.4" })
469
+ ] }),
470
+ trash: /* @__PURE__ */ jsx("path", { d: "M4 7h16M9 7V4.5h6V7M6.5 7l1 13h9l1-13" }),
471
+ pencil: /* @__PURE__ */ jsx("path", { d: "M4 20h4L20 8l-4-4L4 16v4Z" }),
472
+ eye: /* @__PURE__ */ jsxs(Fragment, { children: [
473
+ /* @__PURE__ */ jsx("path", { d: "M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z" }),
474
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
475
+ ] }),
476
+ copy: /* @__PURE__ */ jsxs(Fragment, { children: [
477
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
478
+ /* @__PURE__ */ jsx("path", { d: "M15 6.5V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h.5" })
479
+ ] }),
480
+ clone: /* @__PURE__ */ jsxs(Fragment, { children: [
481
+ /* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "11", height: "11", rx: "2" }),
482
+ /* @__PURE__ */ jsx("path", { d: "M9 20h9a2 2 0 0 0 2-2V9" })
483
+ ] }),
484
+ arrowUpRight: /* @__PURE__ */ jsx("path", { d: "M7 17 17 7M9 7h8v8" }),
485
+ arrowLeft: /* @__PURE__ */ jsx("path", { d: "M19 12H6M11 6l-5 6 5 6" }),
486
+ sort: /* @__PURE__ */ jsx("path", { d: "M8 9 12 5l4 4M8 15l4 4 4-4" }),
487
+ sortAsc: /* @__PURE__ */ jsx("path", { d: "M12 19V6M7 11l5-5 5 5" }),
488
+ sortDesc: /* @__PURE__ */ jsx("path", { d: "M12 5v13M7 13l5 5 5-5" }),
489
+ database: /* @__PURE__ */ jsxs(Fragment, { children: [
490
+ /* @__PURE__ */ jsx("ellipse", { cx: "12", cy: "6", rx: "8", ry: "3.2" }),
491
+ /* @__PURE__ */ jsx("path", { d: "M4 6v12c0 1.8 3.6 3.2 8 3.2s8-1.4 8-3.2V6M4 12c0 1.8 3.6 3.2 8 3.2s8-1.4 8-3.2" })
492
+ ] }),
493
+ table: /* @__PURE__ */ jsxs(Fragment, { children: [
494
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2" }),
495
+ /* @__PURE__ */ jsx("path", { d: "M3 9.5h18M9 9.5V20" })
496
+ ] }),
497
+ users: /* @__PURE__ */ jsxs(Fragment, { children: [
498
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "8", r: "3.2" }),
499
+ /* @__PURE__ */ jsx("path", { d: "M3 19c0-3 2.7-4.8 6-4.8s6 1.8 6 4.8" }),
500
+ /* @__PURE__ */ jsx("path", { d: "M16 5.5a3 3 0 0 1 0 5.6M17.5 14.6c2 .6 3.5 2.1 3.5 4.4" })
501
+ ] }),
502
+ box: /* @__PURE__ */ jsxs(Fragment, { children: [
503
+ /* @__PURE__ */ jsx("path", { d: "M12 3 20.5 7.5v9L12 21l-8.5-4.5v-9L12 3Z" }),
504
+ /* @__PURE__ */ jsx("path", { d: "M3.5 7.5 12 12l8.5-4.5M12 12v9" })
505
+ ] }),
506
+ receipt: /* @__PURE__ */ jsxs(Fragment, { children: [
507
+ /* @__PURE__ */ jsx("path", { d: "M5 3h14v18l-2.3-1.6-2.3 1.6-2.4-1.6L9.6 21l-2.3-1.6L5 21V3Z" }),
508
+ /* @__PURE__ */ jsx("path", { d: "M9 8h6M9 12h6" })
509
+ ] }),
510
+ key: /* @__PURE__ */ jsxs(Fragment, { children: [
511
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "13", r: "4" }),
512
+ /* @__PURE__ */ jsx("path", { d: "m11 11 8-8M17 5l2 2M15 7l2 2" })
513
+ ] }),
514
+ shield: /* @__PURE__ */ jsxs(Fragment, { children: [
515
+ /* @__PURE__ */ jsx("path", { d: "M12 3 20 6v6c0 4.4-3.3 7.9-8 9-4.7-1.1-8-4.6-8-9V6l8-3Z" }),
516
+ /* @__PURE__ */ jsx("path", { d: "m9 12 2.2 2.2L15.5 10" })
517
+ ] }),
518
+ activity: /* @__PURE__ */ jsx("path", { d: "M3 12h4l2.5-6 4 12 2.5-6h5" }),
519
+ tag: /* @__PURE__ */ jsxs(Fragment, { children: [
520
+ /* @__PURE__ */ jsx("path", { d: "M3 11.5V4h7.5l10 10-7.5 7.5-10-10Z" }),
521
+ /* @__PURE__ */ jsx("circle", { cx: "7.5", cy: "7.5", r: "1.3" })
522
+ ] }),
523
+ map: /* @__PURE__ */ jsxs(Fragment, { children: [
524
+ /* @__PURE__ */ jsx("path", { d: "M9 4 3 6.5v13L9 17l6 2.5 6-2.5v-13L15 6.5 9 4Z" }),
525
+ /* @__PURE__ */ jsx("path", { d: "M9 4v13M15 6.5v13" })
526
+ ] }),
527
+ calendar: /* @__PURE__ */ jsxs(Fragment, { children: [
528
+ /* @__PURE__ */ jsx("rect", { x: "3.5", y: "5", width: "17", height: "15", rx: "2" }),
529
+ /* @__PURE__ */ jsx("path", { d: "M3.5 10h17M8 3v4M16 3v4" })
530
+ ] }),
531
+ mail: /* @__PURE__ */ jsxs(Fragment, { children: [
532
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
533
+ /* @__PURE__ */ jsx("path", { d: "m3.5 7 8.5 6 8.5-6" })
534
+ ] }),
535
+ file: /* @__PURE__ */ jsxs(Fragment, { children: [
536
+ /* @__PURE__ */ jsx("path", { d: "M13 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V9l-6-6Z" }),
537
+ /* @__PURE__ */ jsx("path", { d: "M13 3v6h6" })
538
+ ] }),
539
+ alert: /* @__PURE__ */ jsxs(Fragment, { children: [
540
+ /* @__PURE__ */ jsx("path", { d: "M12 4 2.5 20h19L12 4Z" }),
541
+ /* @__PURE__ */ jsx("path", { d: "M12 10v4.5M12 17.4v.2" })
542
+ ] })
543
+ };
544
+ function Icon({
545
+ name,
546
+ size = 15,
547
+ className,
548
+ title
549
+ }) {
550
+ return /* @__PURE__ */ jsxs(
551
+ "svg",
552
+ {
553
+ viewBox: "0 0 24 24",
554
+ width: size,
555
+ height: size,
556
+ className: className ? `mst-icon ${className}` : "mst-icon",
557
+ "aria-hidden": title ? void 0 : true,
558
+ role: title ? "img" : void 0,
559
+ focusable: "false",
560
+ children: [
561
+ title && /* @__PURE__ */ jsx("title", { children: title }),
562
+ PATHS[name]
563
+ ]
564
+ }
565
+ );
566
+ }
567
+ var ENTITY_ICON_HINTS = [
568
+ [["user", "usuario", "people", "pessoa", "customer", "cliente", "associado", "member", "contact"], "users"],
569
+ [["invoice", "fatura", "cobranca", "billing", "receipt", "nota"], "receipt"],
570
+ [["payment", "pagamento", "transaction", "transacao", "ledger"], "table"],
571
+ [["product", "produto", "item", "sku", "inventory", "estoque", "catalog"], "box"],
572
+ [["address", "endereco", "location", "local", "region", "city", "cidade"], "map"],
573
+ [["role", "papel", "permission", "permissao", "grant", "token", "credential"], "key"],
574
+ [["audit", "auditoria", "log", "event", "evento", "metric"], "activity"],
575
+ [["policy", "politica", "security", "seguranca", "consent", "consentimento"], "shield"],
576
+ [["tag", "label", "category", "categoria", "plan", "plano", "subscription", "assinatura"], "tag"],
577
+ [["schedule", "agenda", "appointment", "date", "periodo", "calendar"], "calendar"],
578
+ [["message", "mensagem", "email", "notification", "notificacao", "ticket"], "mail"],
579
+ [["document", "documento", "file", "arquivo", "contract", "contrato", "report"], "file"]
580
+ ];
581
+ function fold(text) {
582
+ return text.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
583
+ }
584
+ function entityIcon(entity) {
585
+ const haystack = fold(`${entity.id} ${entity.table} ${entity.label.singular} ${entity.label.plural}`);
586
+ for (const [words, icon] of ENTITY_ICON_HINTS) {
587
+ if (words.some((word) => haystack.includes(word))) return icon;
588
+ }
589
+ return "table";
590
+ }
591
+
592
+ // src/components/brand.tsx
593
+ import { useId } from "react";
594
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
595
+ function labelProps(title) {
596
+ return title ? { role: "img" } : { "aria-hidden": true };
597
+ }
598
+ function MaestroMark({
599
+ size = 30,
600
+ className,
601
+ title
602
+ }) {
603
+ const maskId = useId();
604
+ return /* @__PURE__ */ jsxs2(
605
+ "svg",
606
+ {
607
+ viewBox: "0 0 64 64",
608
+ width: size,
609
+ height: size,
610
+ fill: "none",
611
+ className,
612
+ focusable: "false",
613
+ ...labelProps(title),
614
+ children: [
615
+ title && /* @__PURE__ */ jsx2("title", { children: title }),
616
+ /* @__PURE__ */ jsxs2("mask", { id: maskId, maskUnits: "userSpaceOnUse", x: "0", y: "0", width: "64", height: "64", children: [
617
+ /* @__PURE__ */ jsx2("rect", { width: "64", height: "64", fill: "#000" }),
618
+ /* @__PURE__ */ jsx2("rect", { y: "14", width: "64", height: "10.667", fill: "#fff" }),
619
+ /* @__PURE__ */ jsx2("rect", { y: "28.667", width: "64", height: "10.667", fill: "#fff" }),
620
+ /* @__PURE__ */ jsx2("rect", { y: "43.333", width: "64", height: "10.667", fill: "#fff" })
621
+ ] }),
622
+ /* @__PURE__ */ jsx2(
623
+ "path",
624
+ {
625
+ d: "M12 54V14l20 24 20-24v40",
626
+ stroke: "currentColor",
627
+ strokeWidth: "10",
628
+ strokeLinejoin: "round",
629
+ mask: `url(#${maskId})`
630
+ }
631
+ )
632
+ ]
633
+ }
634
+ );
635
+ }
636
+ function MaestroSeal({
637
+ size = 30,
638
+ className,
639
+ title
640
+ }) {
641
+ const maskId = useId();
642
+ return /* @__PURE__ */ jsxs2(
643
+ "svg",
644
+ {
645
+ viewBox: "0 0 64 64",
646
+ width: size,
647
+ height: size,
648
+ className,
649
+ focusable: "false",
650
+ ...labelProps(title),
651
+ children: [
652
+ title && /* @__PURE__ */ jsx2("title", { children: title }),
653
+ /* @__PURE__ */ jsxs2("mask", { id: maskId, maskUnits: "userSpaceOnUse", x: "0", y: "0", width: "64", height: "64", children: [
654
+ /* @__PURE__ */ jsx2("rect", { width: "64", height: "64", fill: "#fff" }),
655
+ /* @__PURE__ */ jsxs2("g", { transform: "scale(1.142857143) translate(-4 -4)", children: [
656
+ /* @__PURE__ */ jsx2("path", { d: "M17.3 47V17L32 35L46.7 17V47", fill: "none", stroke: "#000", strokeWidth: "8.1", strokeLinejoin: "round" }),
657
+ /* @__PURE__ */ jsx2("rect", { x: "-8", y: "-8", width: "80", height: "25", fill: "#fff" }),
658
+ /* @__PURE__ */ jsx2("rect", { x: "-8", y: "47", width: "80", height: "25", fill: "#fff" }),
659
+ /* @__PURE__ */ jsx2("rect", { x: "-8", y: "25", width: "80", height: "3", fill: "#fff" })
660
+ ] })
661
+ ] }),
662
+ /* @__PURE__ */ jsx2("rect", { width: "64", height: "64", rx: "18.3", fill: "currentColor", mask: `url(#${maskId})` })
663
+ ]
664
+ }
665
+ );
666
+ }
667
+
668
+ // src/components/Sidebar.tsx
669
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
366
670
  function normalize(text) {
367
671
  return text.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
368
672
  }
@@ -381,9 +685,9 @@ function Highlight({ label, query }) {
381
685
  if (idx === -1) return label;
382
686
  const start = originalIndex[idx];
383
687
  const end = (originalIndex[idx + foldedQuery.length - 1] ?? label.length - 1) + 1;
384
- return /* @__PURE__ */ jsxs(Fragment, { children: [
688
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
385
689
  label.slice(0, start),
386
- /* @__PURE__ */ jsx("mark", { className: "rounded-sm bg-amber-200 text-inherit", children: label.slice(start, end) }),
690
+ /* @__PURE__ */ jsx3("mark", { children: label.slice(start, end) }),
387
691
  label.slice(end)
388
692
  ] });
389
693
  }
@@ -394,33 +698,40 @@ function EntityButton({
394
698
  subtitle,
395
699
  query
396
700
  }) {
397
- return /* @__PURE__ */ jsxs(
701
+ return /* @__PURE__ */ jsxs3(
398
702
  "button",
399
703
  {
704
+ type: "button",
705
+ "aria-current": active,
400
706
  onClick: () => onSelect(entity.id),
401
- className: `flex w-full flex-col rounded-md px-3 py-1.5 text-left text-sm transition-colors ${active ? "bg-indigo-100 font-medium text-indigo-700" : "text-slate-700 hover:bg-slate-100"}`,
707
+ className: "mst-navitem",
708
+ title: entity.label.plural,
402
709
  children: [
403
- /* @__PURE__ */ jsx("span", { className: "truncate", children: query ? /* @__PURE__ */ jsx(Highlight, { label: entity.label.plural, query }) : entity.label.plural }),
404
- subtitle && /* @__PURE__ */ jsx("span", { className: "truncate text-xs font-normal text-slate-400", children: subtitle })
710
+ /* @__PURE__ */ jsx3(Icon, { name: entityIcon(entity), size: 14 }),
711
+ /* @__PURE__ */ jsxs3("span", { className: "mst-navitem__label", children: [
712
+ query ? /* @__PURE__ */ jsx3(Highlight, { label: entity.label.plural, query }) : entity.label.plural,
713
+ subtitle && /* @__PURE__ */ jsxs3("span", { className: "mst-navitem__sub", children: [
714
+ " \xB7 ",
715
+ subtitle
716
+ ] })
717
+ ] })
405
718
  ]
406
719
  }
407
720
  );
408
721
  }
409
- function SectionTitle({ children }) {
410
- return /* @__PURE__ */ jsx("div", { className: "px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-slate-400", children });
411
- }
412
722
  function Sidebar({
413
723
  entities,
414
724
  activeId,
415
725
  onSelect,
416
726
  title,
417
727
  labels,
418
- recents = []
728
+ recents = [],
729
+ onHome,
730
+ homeActive = false
419
731
  }) {
420
732
  const t = useT();
421
733
  const [query, setQuery] = useState2("");
422
734
  const [expanded, setExpanded] = useState2({});
423
- const searchRef = useRef(null);
424
735
  const menu = useMemo(() => buildMenu(entities, labels), [entities, labels]);
425
736
  const groupOf = useMemo(() => {
426
737
  const map = /* @__PURE__ */ new Map();
@@ -433,27 +744,19 @@ function Sidebar({
433
744
  const q = normalize(query);
434
745
  return entities.filter((e) => normalize(`${e.label.plural} ${e.label.singular} ${e.table}`).includes(q)).sort((a, b) => a.label.plural.localeCompare(b.label.plural));
435
746
  }, [entities, query]);
436
- useEffect2(() => {
437
- const onKey = (e) => {
438
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
439
- e.preventDefault();
440
- searchRef.current?.focus();
441
- }
442
- };
443
- document.addEventListener("keydown", onKey);
444
- return () => document.removeEventListener("keydown", onKey);
445
- }, []);
446
747
  const select = (entityId) => {
447
748
  setQuery("");
448
749
  onSelect(entityId);
449
750
  };
450
751
  const isExpanded = (groupId, hasActive) => expanded[groupId] ?? hasActive;
451
- return /* @__PURE__ */ jsxs("aside", { className: "flex w-64 shrink-0 flex-col border-r border-slate-200 bg-slate-50", children: [
452
- /* @__PURE__ */ jsx("div", { className: "border-b border-slate-200 px-4 py-4", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tracking-tight text-slate-900", children: title }) }),
453
- /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: /* @__PURE__ */ jsx(
752
+ return /* @__PURE__ */ jsxs3("aside", { className: "mst-side", children: [
753
+ /* @__PURE__ */ jsxs3("div", { className: "mst-side__brand", children: [
754
+ /* @__PURE__ */ jsx3(MaestroMark, { className: "mst-mark", size: 30 }),
755
+ /* @__PURE__ */ jsx3("span", { className: "mst-side__title", children: title })
756
+ ] }),
757
+ /* @__PURE__ */ jsx3("div", { className: "mst-side__search", children: /* @__PURE__ */ jsx3(
454
758
  "input",
455
759
  {
456
- ref: searchRef,
457
760
  type: "search",
458
761
  value: query,
459
762
  onChange: (e) => setQuery(e.target.value),
@@ -461,11 +764,11 @@ function Sidebar({
461
764
  if (e.key === "Escape") setQuery("");
462
765
  if (e.key === "Enter" && results[0]) select(results[0].id);
463
766
  },
464
- placeholder: `${t.searchMenu} (Ctrl+K)`,
465
- className: "w-full rounded-md border border-slate-300 bg-white px-3 py-1.5 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
767
+ placeholder: t.searchMenu,
768
+ "aria-label": t.searchMenu
466
769
  }
467
770
  ) }),
468
- /* @__PURE__ */ jsx("nav", { className: "flex-1 overflow-y-auto p-2", children: query ? results.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-sm text-slate-400", children: t.noMenuResults }) : results.map((e) => /* @__PURE__ */ jsx(
771
+ /* @__PURE__ */ jsx3("nav", { className: "mst-side__nav", children: query ? results.length === 0 ? /* @__PURE__ */ jsx3("p", { className: "mst-side__empty", children: t.noMenuResults }) : results.map((e) => /* @__PURE__ */ jsx3(
469
772
  EntityButton,
470
773
  {
471
774
  entity: e,
@@ -475,90 +778,615 @@ function Sidebar({
475
778
  query
476
779
  },
477
780
  e.id
478
- )) : /* @__PURE__ */ jsxs(Fragment, { children: [
479
- recentEntities.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
480
- /* @__PURE__ */ jsx(SectionTitle, { children: t.recents }),
481
- recentEntities.map((e) => /* @__PURE__ */ jsx(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id))
781
+ )) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
782
+ onHome && /* @__PURE__ */ jsxs3("button", { type: "button", className: "mst-navitem", "aria-current": homeActive, onClick: onHome, title: t.hub, children: [
783
+ /* @__PURE__ */ jsx3(Icon, { name: "hub", size: 14 }),
784
+ /* @__PURE__ */ jsx3("span", { className: "mst-navitem__label", children: t.hub })
482
785
  ] }),
483
- /* @__PURE__ */ jsx(SectionTitle, { children: t.menus }),
786
+ recentEntities.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
787
+ /* @__PURE__ */ jsx3("p", { className: "mst-side__section", children: t.recents }),
788
+ recentEntities.map((e) => /* @__PURE__ */ jsx3(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id))
789
+ ] }),
790
+ /* @__PURE__ */ jsx3("p", { className: "mst-side__section", children: t.menus }),
484
791
  menu.groups.map((group) => {
485
792
  const hasActive = group.entities.some((e) => e.id === activeId);
486
793
  const open = isExpanded(group.id, hasActive);
487
- return /* @__PURE__ */ jsxs("div", { children: [
488
- /* @__PURE__ */ jsxs(
794
+ return /* @__PURE__ */ jsxs3("div", { children: [
795
+ /* @__PURE__ */ jsxs3(
489
796
  "button",
490
797
  {
798
+ type: "button",
799
+ "aria-expanded": open,
491
800
  onClick: () => setExpanded((prev) => ({ ...prev, [group.id]: !open })),
492
- className: "flex w-full items-center gap-1.5 rounded-md px-3 py-1.5 text-left text-sm font-medium text-slate-600 hover:bg-slate-100",
801
+ className: "mst-navitem mst-navitem--folder",
493
802
  children: [
494
- /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-400", children: open ? "\u25BE" : "\u25B8" }),
495
- /* @__PURE__ */ jsx("span", { className: "truncate", children: group.label }),
496
- /* @__PURE__ */ jsx("span", { className: "ml-auto text-xs text-slate-400", children: group.entities.length })
803
+ /* @__PURE__ */ jsx3(
804
+ Icon,
805
+ {
806
+ name: "chevronRight",
807
+ size: 12,
808
+ className: open ? "mst-navitem__chev mst-navitem__chev--open" : "mst-navitem__chev"
809
+ }
810
+ ),
811
+ /* @__PURE__ */ jsx3("span", { className: "mst-navitem__label", children: group.label }),
812
+ /* @__PURE__ */ jsx3("span", { className: "mst-navitem__count", children: group.entities.length })
497
813
  ]
498
814
  }
499
815
  ),
500
- open && /* @__PURE__ */ jsx("div", { className: "ml-3 border-l border-slate-200 pl-1", children: group.entities.map((e) => /* @__PURE__ */ jsx(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id)) })
816
+ open && /* @__PURE__ */ jsx3("div", { className: "mst-side__children", children: group.entities.map((e) => /* @__PURE__ */ jsx3(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id)) })
501
817
  ] }, group.id);
502
818
  }),
503
- menu.ungrouped.map((e) => /* @__PURE__ */ jsx(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id))
819
+ menu.ungrouped.map((e) => /* @__PURE__ */ jsx3(EntityButton, { entity: e, active: e.id === activeId, onSelect: select }, e.id))
504
820
  ] }) }),
505
- /* @__PURE__ */ jsx("div", { className: "border-t border-slate-200 px-4 py-3 text-xs text-slate-400", children: t.entitiesFooter(entities.length) })
821
+ /* @__PURE__ */ jsxs3("div", { className: "mst-side__foot", children: [
822
+ /* @__PURE__ */ jsx3("span", { className: "mst-pulse", "aria-hidden": true }),
823
+ /* @__PURE__ */ jsx3("span", { children: t.entitiesFooter(entities.length) })
824
+ ] })
506
825
  ] });
507
826
  }
508
827
 
509
- // src/components/EntityList.tsx
510
- import { useMemo as useMemo2, useState as useState5 } from "react";
828
+ // src/components/Header.tsx
829
+ import { useState as useState5 } from "react";
511
830
 
512
- // src/components/ui.tsx
513
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
514
- var VARIANTS = {
515
- primary: "bg-indigo-600 text-white hover:bg-indigo-500 disabled:bg-indigo-300",
516
- secondary: "bg-white text-slate-700 ring-1 ring-inset ring-slate-300 hover:bg-slate-50",
517
- danger: "bg-red-600 text-white hover:bg-red-500 disabled:bg-red-300",
518
- ghost: "text-slate-600 hover:bg-slate-100"
831
+ // src/theme.tsx
832
+ import {
833
+ createContext as createContext3,
834
+ useCallback as useCallback2,
835
+ useContext as useContext3,
836
+ useEffect as useEffect2,
837
+ useMemo as useMemo2,
838
+ useRef,
839
+ useState as useState3
840
+ } from "react";
841
+ import { jsx as jsx4 } from "react/jsx-runtime";
842
+ var MAESTRO_THEMES = [
843
+ { id: "cobalto", label: "Grafite & Cobalto", swatch: "#3557e8" },
844
+ { id: "latao", label: "Ard\xF3sia & Lat\xE3o", swatch: "#9a6a22" },
845
+ { id: "teal", label: "Tinta & Teal", swatch: "#0e7c74" }
846
+ ];
847
+ var STORAGE_KEY = "maestro-admin:appearance";
848
+ var DEFAULTS = {
849
+ preset: "cobalto",
850
+ scheme: "system",
851
+ density: "comfortable"
852
+ };
853
+ function normalizeConfig(input) {
854
+ if (!input) return {};
855
+ return typeof input === "string" ? { preset: input } : input;
856
+ }
857
+ function readStored() {
858
+ try {
859
+ const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
860
+ const parsed = raw ? JSON.parse(raw) : null;
861
+ return parsed && typeof parsed === "object" ? parsed : {};
862
+ } catch {
863
+ return {};
864
+ }
865
+ }
866
+ function writeStored(value) {
867
+ try {
868
+ globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(value));
869
+ } catch {
870
+ }
871
+ }
872
+ var ThemeContext = createContext3({
873
+ ...DEFAULTS,
874
+ picker: true,
875
+ setPreset: () => void 0,
876
+ setScheme: () => void 0,
877
+ setDensity: () => void 0
878
+ });
879
+ function useTheme() {
880
+ return useContext3(ThemeContext);
881
+ }
882
+ function MaestroThemeProvider({
883
+ theme,
884
+ className,
885
+ children,
886
+ rootRef
887
+ }) {
888
+ const config = useMemo2(() => normalizeConfig(theme), [theme]);
889
+ const remember = config.remember !== false;
890
+ const [state, setState] = useState3(() => {
891
+ const stored = remember ? readStored() : {};
892
+ return {
893
+ preset: stored.preset ?? config.preset ?? DEFAULTS.preset,
894
+ scheme: stored.scheme ?? config.scheme ?? DEFAULTS.scheme,
895
+ density: stored.density ?? config.density ?? DEFAULTS.density
896
+ };
897
+ });
898
+ const update = useCallback2(
899
+ (patch) => {
900
+ setState((prev) => {
901
+ const next = { ...prev, ...patch };
902
+ if (remember) writeStored(next);
903
+ return next;
904
+ });
905
+ },
906
+ [remember]
907
+ );
908
+ const value = useMemo2(
909
+ () => ({
910
+ preset: state.preset ?? DEFAULTS.preset,
911
+ scheme: state.scheme ?? DEFAULTS.scheme,
912
+ density: state.density ?? DEFAULTS.density,
913
+ picker: config.picker !== false,
914
+ setPreset: (preset) => update({ preset }),
915
+ setScheme: (scheme) => update({ scheme }),
916
+ setDensity: (density) => update({ density })
917
+ }),
918
+ [state, config.picker, update]
919
+ );
920
+ const faviconRef = useRef(config.favicon);
921
+ faviconRef.current = config.favicon;
922
+ useEffect2(() => {
923
+ const href = faviconRef.current?.(value.preset);
924
+ if (!href) return;
925
+ const link = globalThis.document?.querySelector('link[rel="icon"]');
926
+ if (link) link.href = href;
927
+ }, [value.preset]);
928
+ return /* @__PURE__ */ jsx4(ThemeContext.Provider, { value, children: /* @__PURE__ */ jsx4(
929
+ "div",
930
+ {
931
+ ref: rootRef,
932
+ className,
933
+ "data-maestro-theme": value.preset,
934
+ "data-maestro-scheme": value.scheme,
935
+ "data-maestro-density": value.density,
936
+ style: config.tokens,
937
+ children
938
+ }
939
+ ) });
940
+ }
941
+
942
+ // src/components/menu.tsx
943
+ import { useEffect as useEffect3, useLayoutEffect, useRef as useRef2, useState as useState4 } from "react";
944
+ import { createPortal } from "react-dom";
945
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
946
+ function PopMenu({ anchor, items, onClose }) {
947
+ const ref = useRef2(null);
948
+ const [pos, setPos] = useState4(anchor);
949
+ const root = useAdminRoot();
950
+ useLayoutEffect(() => {
951
+ const el = ref.current;
952
+ if (!el) return;
953
+ const rect = el.getBoundingClientRect();
954
+ setPos({
955
+ x: Math.max(4, Math.min(anchor.x, window.innerWidth - rect.width - 8)),
956
+ y: Math.max(4, Math.min(anchor.y, window.innerHeight - rect.height - 8))
957
+ });
958
+ }, [anchor]);
959
+ useEffect3(() => {
960
+ const onDown = (e) => {
961
+ if (ref.current && !ref.current.contains(e.target)) onClose();
962
+ };
963
+ const onKey = (e) => {
964
+ if (e.key === "Escape") onClose();
965
+ };
966
+ document.addEventListener("mousedown", onDown);
967
+ document.addEventListener("keydown", onKey);
968
+ window.addEventListener("scroll", onClose, true);
969
+ window.addEventListener("resize", onClose);
970
+ return () => {
971
+ document.removeEventListener("mousedown", onDown);
972
+ document.removeEventListener("keydown", onKey);
973
+ window.removeEventListener("scroll", onClose, true);
974
+ window.removeEventListener("resize", onClose);
975
+ };
976
+ }, [onClose]);
977
+ return createPortal(
978
+ /* @__PURE__ */ jsx5("div", { ref, role: "menu", style: { left: pos.x, top: pos.y }, className: "mst-menu", children: items.map((item) => /* @__PURE__ */ jsxs4("div", { children: [
979
+ item.separated && /* @__PURE__ */ jsx5("div", { className: "mst-menu__sep" }),
980
+ /* @__PURE__ */ jsxs4(
981
+ "button",
982
+ {
983
+ type: "button",
984
+ role: "menuitem",
985
+ onClick: () => {
986
+ onClose();
987
+ item.onSelect();
988
+ },
989
+ className: `mst-menu__item${item.danger ? " mst-menu__item--danger" : ""}`,
990
+ children: [
991
+ item.icon && /* @__PURE__ */ jsx5(Icon, { name: item.icon, size: 14 }),
992
+ item.label
993
+ ]
994
+ }
995
+ )
996
+ ] }, item.id)) }),
997
+ root ?? document.body
998
+ );
999
+ }
1000
+ function MenuButton({ items, label }) {
1001
+ const [anchor, setAnchor] = useState4(void 0);
1002
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
1003
+ /* @__PURE__ */ jsx5(
1004
+ "button",
1005
+ {
1006
+ type: "button",
1007
+ "aria-label": label,
1008
+ title: label,
1009
+ onClick: (e) => {
1010
+ const rect = e.currentTarget.getBoundingClientRect();
1011
+ setAnchor({ x: rect.right - 184, y: rect.bottom + 4 });
1012
+ },
1013
+ className: "mst-iconbtn",
1014
+ children: /* @__PURE__ */ jsx5(Icon, { name: "dots", size: 14 })
1015
+ }
1016
+ ),
1017
+ anchor && /* @__PURE__ */ jsx5(PopMenu, { anchor, items, onClose: () => setAnchor(void 0) })
1018
+ ] });
1019
+ }
1020
+
1021
+ // src/components/Header.tsx
1022
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1023
+ var NEXT_SCHEME = { system: "light", light: "dark", dark: "system" };
1024
+ var SCHEME_ICON = {
1025
+ system: "swatch",
1026
+ light: "sun",
1027
+ dark: "moon"
519
1028
  };
1029
+ function Header({ crumbs, onOpenPalette }) {
1030
+ const t = useT();
1031
+ const theme = useTheme();
1032
+ const [anchor, setAnchor] = useState5(void 0);
1033
+ const themeItems = MAESTRO_THEMES.map((option) => ({
1034
+ id: option.id,
1035
+ label: /* @__PURE__ */ jsxs5(Fragment4, { children: [
1036
+ /* @__PURE__ */ jsx6("span", { className: "mst-swatch", style: { background: option.swatch } }),
1037
+ option.label,
1038
+ theme.preset === option.id && /* @__PURE__ */ jsx6(Icon, { name: "check", size: 13 })
1039
+ ] }),
1040
+ onSelect: () => theme.setPreset(option.id)
1041
+ }));
1042
+ const schemeLabel = theme.scheme === "system" ? t.schemeSystem : theme.scheme === "light" ? t.schemeLight : t.schemeDark;
1043
+ return /* @__PURE__ */ jsxs5("header", { className: "mst-header", children: [
1044
+ /* @__PURE__ */ jsx6("nav", { className: "mst-crumbs", "aria-label": t.breadcrumb, children: crumbs.map((crumb, index) => /* @__PURE__ */ jsxs5("span", { className: "mst-crumbs__item", children: [
1045
+ index > 0 && /* @__PURE__ */ jsx6(Icon, { name: "chevronRight", size: 12, className: "mst-crumbs__sep" }),
1046
+ crumb.onClick ? /* @__PURE__ */ jsxs5("button", { type: "button", className: "mst-crumbs__link", onClick: crumb.onClick, children: [
1047
+ crumb.icon && /* @__PURE__ */ jsx6(Icon, { name: crumb.icon, size: 13 }),
1048
+ crumb.label
1049
+ ] }) : /* @__PURE__ */ jsxs5("span", { className: "mst-crumbs__now", children: [
1050
+ crumb.icon && /* @__PURE__ */ jsx6(Icon, { name: crumb.icon, size: 13 }),
1051
+ crumb.label
1052
+ ] })
1053
+ ] }, `${crumb.label}-${index}`)) }),
1054
+ /* @__PURE__ */ jsxs5("button", { type: "button", className: "mst-searchchip", onClick: onOpenPalette, children: [
1055
+ /* @__PURE__ */ jsx6(Icon, { name: "search", size: 13 }),
1056
+ /* @__PURE__ */ jsx6("span", { children: t.search }),
1057
+ /* @__PURE__ */ jsx6("kbd", { className: "mst-kbd", children: t.commandKey })
1058
+ ] }),
1059
+ theme.picker && /* @__PURE__ */ jsxs5(Fragment4, { children: [
1060
+ /* @__PURE__ */ jsx6(
1061
+ "button",
1062
+ {
1063
+ type: "button",
1064
+ "aria-label": t.density,
1065
+ title: t.density,
1066
+ "aria-pressed": theme.density === "compact",
1067
+ className: "mst-iconbtn",
1068
+ onClick: () => theme.setDensity(theme.density === "compact" ? "comfortable" : "compact"),
1069
+ children: /* @__PURE__ */ jsx6(Icon, { name: "rows", size: 14 })
1070
+ }
1071
+ ),
1072
+ /* @__PURE__ */ jsx6(
1073
+ "button",
1074
+ {
1075
+ type: "button",
1076
+ "aria-label": t.palette,
1077
+ title: t.palette,
1078
+ className: "mst-iconbtn",
1079
+ onClick: (e) => {
1080
+ const rect = e.currentTarget.getBoundingClientRect();
1081
+ setAnchor({ x: rect.right - 200, y: rect.bottom + 6 });
1082
+ },
1083
+ children: /* @__PURE__ */ jsx6(Icon, { name: "swatch", size: 14 })
1084
+ }
1085
+ ),
1086
+ /* @__PURE__ */ jsx6(
1087
+ "button",
1088
+ {
1089
+ type: "button",
1090
+ "aria-label": `${t.colorScheme}: ${schemeLabel}`,
1091
+ title: `${t.colorScheme}: ${schemeLabel}`,
1092
+ className: "mst-iconbtn",
1093
+ onClick: () => theme.setScheme(NEXT_SCHEME[theme.scheme]),
1094
+ children: /* @__PURE__ */ jsx6(Icon, { name: SCHEME_ICON[theme.scheme], size: 14 })
1095
+ }
1096
+ )
1097
+ ] }),
1098
+ anchor && /* @__PURE__ */ jsx6(PopMenu, { anchor, items: themeItems, onClose: () => setAnchor(void 0) })
1099
+ ] });
1100
+ }
1101
+
1102
+ // src/components/Hub.tsx
1103
+ import { useMemo as useMemo3 } from "react";
1104
+
1105
+ // src/components/ui.tsx
1106
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
520
1107
  function Button({
521
1108
  variant = "secondary",
1109
+ icon,
522
1110
  className = "",
1111
+ children,
523
1112
  ...props
524
1113
  }) {
525
- return /* @__PURE__ */ jsx2(
526
- "button",
527
- {
528
- className: `inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed ${VARIANTS[variant]} ${className}`,
529
- ...props
530
- }
531
- );
1114
+ return /* @__PURE__ */ jsxs6("button", { type: "button", className: `mst-btn mst-btn--${variant} ${className}`.trim(), ...props, children: [
1115
+ icon && /* @__PURE__ */ jsx7(Icon, { name: icon, size: 14 }),
1116
+ children
1117
+ ] });
532
1118
  }
533
- function Badge({ children, tone = "slate" }) {
534
- const tones = {
535
- slate: "bg-slate-100 text-slate-700",
536
- green: "bg-green-100 text-green-700",
537
- red: "bg-red-100 text-red-700",
538
- amber: "bg-amber-100 text-amber-700",
539
- indigo: "bg-indigo-100 text-indigo-700"
540
- };
541
- return /* @__PURE__ */ jsx2("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${tones[tone]}`, children });
1119
+ function IconButton({
1120
+ icon,
1121
+ label,
1122
+ className = "",
1123
+ ...props
1124
+ }) {
1125
+ return /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": label, title: label, className: `mst-iconbtn ${className}`.trim(), ...props, children: /* @__PURE__ */ jsx7(Icon, { name: icon, size: 14 }) });
1126
+ }
1127
+ function Badge({ children, tone = "neutral", dot = false }) {
1128
+ const toneClass = tone === "neutral" ? "" : ` mst-badge--${tone}`;
1129
+ return /* @__PURE__ */ jsx7("span", { className: `mst-badge${toneClass}${dot ? " mst-badge--dot" : ""}`, children });
542
1130
  }
543
1131
  function Spinner({ label }) {
544
- return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-sm text-slate-500", role: "status", children: [
545
- /* @__PURE__ */ jsx2("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-slate-300 border-t-indigo-600" }),
546
- label ?? "Loading\u2026"
547
- ] });
1132
+ return /* @__PURE__ */ jsx7("div", { className: "mst-spinner", role: "status", children: label ?? "Loading\u2026" });
548
1133
  }
549
1134
  function EmptyState({ title, hint }) {
550
- return /* @__PURE__ */ jsxs2("div", { className: "rounded-lg border border-dashed border-slate-300 p-8 text-center", children: [
551
- /* @__PURE__ */ jsx2("p", { className: "text-sm font-medium text-slate-700", children: title }),
552
- hint && /* @__PURE__ */ jsx2("p", { className: "mt-1 text-sm text-slate-500", children: hint })
1135
+ return /* @__PURE__ */ jsxs6("div", { className: "mst-empty", children: [
1136
+ /* @__PURE__ */ jsx7("p", { className: "mst-empty__title", children: title }),
1137
+ hint && /* @__PURE__ */ jsx7("p", { className: "mst-empty__hint", children: hint })
553
1138
  ] });
554
1139
  }
555
1140
  function ErrorBanner({ error }) {
556
- return /* @__PURE__ */ jsx2("div", { className: "rounded-md border border-red-200 bg-red-50 p-3 text-sm text-red-700", role: "alert", children: error.message });
1141
+ return /* @__PURE__ */ jsxs6("div", { className: "mst-error", role: "alert", children: [
1142
+ /* @__PURE__ */ jsx7(Icon, { name: "alert", size: 15 }),
1143
+ /* @__PURE__ */ jsx7("span", { children: error.message })
1144
+ ] });
1145
+ }
1146
+ function Toast({ children }) {
1147
+ return /* @__PURE__ */ jsx7("div", { className: "mst-toast", role: "status", children });
1148
+ }
1149
+
1150
+ // src/components/Hub.tsx
1151
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1152
+ var CAPS = ["list", "create", "update", "delete"];
1153
+ function EntityCard({ entity, onOpen }) {
1154
+ const t = useT();
1155
+ const capLabel = { list: t.capRead, create: t.capCreate, update: t.capUpdate, delete: t.capDelete };
1156
+ return /* @__PURE__ */ jsxs7("button", { type: "button", className: "mst-ecard", onClick: () => onOpen(entity.id), children: [
1157
+ /* @__PURE__ */ jsx8("span", { className: "mst-ecard__tile", children: /* @__PURE__ */ jsx8(Icon, { name: entityIcon(entity), size: 16 }) }),
1158
+ /* @__PURE__ */ jsxs7("span", { className: "mst-ecard__body", children: [
1159
+ /* @__PURE__ */ jsx8("span", { className: "mst-ecard__name", children: entity.label.plural }),
1160
+ /* @__PURE__ */ jsx8("span", { className: "mst-ecard__desc", children: entity.description ?? t.fieldCount(entity.fields.length) }),
1161
+ /* @__PURE__ */ jsx8("span", { className: "mst-caps", children: CAPS.map((cap) => /* @__PURE__ */ jsx8("span", { className: "mst-cap", "data-on": String(entity.capabilities[cap]), children: capLabel[cap] }, cap)) }),
1162
+ /* @__PURE__ */ jsxs7("span", { className: "mst-source", children: [
1163
+ /* @__PURE__ */ jsx8(Icon, { name: "database", size: 11 }),
1164
+ /* @__PURE__ */ jsxs7("span", { children: [
1165
+ entity.datasource,
1166
+ " \xB7 ",
1167
+ entity.table
1168
+ ] })
1169
+ ] })
1170
+ ] })
1171
+ ] });
1172
+ }
1173
+ function Hub({
1174
+ entities,
1175
+ labels,
1176
+ onOpen
1177
+ }) {
1178
+ const t = useT();
1179
+ const menu = useMemo3(() => buildMenu(entities, labels), [entities, labels]);
1180
+ const sources = useMemo3(() => new Set(entities.map((e) => e.datasource)).size, [entities]);
1181
+ if (entities.length === 0) {
1182
+ return /* @__PURE__ */ jsx8(EmptyState, { title: t.noEntities, hint: t.noEntitiesHint });
1183
+ }
1184
+ return /* @__PURE__ */ jsxs7("section", { className: "mst-page", children: [
1185
+ /* @__PURE__ */ jsx8("div", { className: "mst-pagehead", children: /* @__PURE__ */ jsxs7("div", { children: [
1186
+ /* @__PURE__ */ jsx8("h1", { className: "mst-pagehead__title", children: t.entities }),
1187
+ /* @__PURE__ */ jsx8("p", { className: "mst-pagehead__meta", children: t.hubMeta(entities.length, sources) })
1188
+ ] }) }),
1189
+ /* @__PURE__ */ jsxs7("div", { className: "mst-hub", children: [
1190
+ menu.groups.map((group) => /* @__PURE__ */ jsxs7("div", { children: [
1191
+ /* @__PURE__ */ jsx8("p", { className: "mst-grouplabel", children: group.label }),
1192
+ /* @__PURE__ */ jsx8("div", { className: "mst-cards", children: group.entities.map((entity) => /* @__PURE__ */ jsx8(EntityCard, { entity, onOpen }, entity.id)) })
1193
+ ] }, group.id)),
1194
+ menu.ungrouped.length > 0 && /* @__PURE__ */ jsxs7("div", { children: [
1195
+ menu.groups.length > 0 && /* @__PURE__ */ jsx8("p", { className: "mst-grouplabel", children: t.otherEntities }),
1196
+ /* @__PURE__ */ jsx8("div", { className: "mst-cards", children: menu.ungrouped.map((entity) => /* @__PURE__ */ jsx8(EntityCard, { entity, onOpen }, entity.id)) })
1197
+ ] })
1198
+ ] })
1199
+ ] });
1200
+ }
1201
+
1202
+ // src/components/CommandPalette.tsx
1203
+ import { useEffect as useEffect4, useMemo as useMemo4, useRef as useRef3, useState as useState6 } from "react";
1204
+ import { createPortal as createPortal2 } from "react-dom";
1205
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1206
+ function normalize2(text) {
1207
+ return text.toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
1208
+ }
1209
+ function Highlight2({ label, query }) {
1210
+ if (!query) return label;
1211
+ const folded = normalize2(label);
1212
+ const idx = folded.indexOf(normalize2(query));
1213
+ if (idx === -1) return label;
1214
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
1215
+ label.slice(0, idx),
1216
+ /* @__PURE__ */ jsx9("mark", { children: label.slice(idx, idx + query.length) }),
1217
+ label.slice(idx + query.length)
1218
+ ] });
1219
+ }
1220
+ function entityCommands(entities, groupOf, onSelect) {
1221
+ return entities.map((entity) => ({
1222
+ id: `entity:${entity.id}`,
1223
+ label: entity.label.plural,
1224
+ icon: entityIcon(entity),
1225
+ hint: groupOf.get(entity.id),
1226
+ keywords: `${entity.label.singular} ${entity.table} ${entity.id}`,
1227
+ run: () => onSelect(entity.id)
1228
+ }));
1229
+ }
1230
+ function CommandPalette({ sections, onClose }) {
1231
+ const t = useT();
1232
+ const root = useAdminRoot();
1233
+ const inputRef = useRef3(null);
1234
+ const listRef = useRef3(null);
1235
+ const [query, setQuery] = useState6("");
1236
+ const [cursor, setCursor] = useState6(0);
1237
+ const visible = useMemo4(() => {
1238
+ if (!query) return sections;
1239
+ const q = normalize2(query);
1240
+ return sections.map((section) => ({
1241
+ ...section,
1242
+ commands: section.commands.filter((c) => normalize2(`${c.label} ${c.keywords ?? ""}`).includes(q))
1243
+ })).filter((section) => section.commands.length > 0);
1244
+ }, [sections, query]);
1245
+ const flat = useMemo4(() => visible.flatMap((section) => section.commands), [visible]);
1246
+ const active = flat[Math.min(cursor, flat.length - 1)];
1247
+ useEffect4(() => setCursor(0), [query]);
1248
+ useEffect4(() => inputRef.current?.focus(), []);
1249
+ useEffect4(() => {
1250
+ listRef.current?.querySelector('[data-active="true"]')?.scrollIntoView({ block: "nearest" });
1251
+ }, [cursor, query]);
1252
+ const onKeyDown = (e) => {
1253
+ if (e.key === "Escape") {
1254
+ e.preventDefault();
1255
+ onClose();
1256
+ return;
1257
+ }
1258
+ if (e.key === "ArrowDown" || e.key === "n" && e.ctrlKey) {
1259
+ e.preventDefault();
1260
+ setCursor((c) => flat.length === 0 ? 0 : (Math.min(c, flat.length - 1) + 1) % flat.length);
1261
+ return;
1262
+ }
1263
+ if (e.key === "ArrowUp" || e.key === "p" && e.ctrlKey) {
1264
+ e.preventDefault();
1265
+ setCursor((c) => flat.length === 0 ? 0 : (Math.min(c, flat.length - 1) + flat.length - 1) % flat.length);
1266
+ return;
1267
+ }
1268
+ if (e.key === "Enter" && active) {
1269
+ e.preventDefault();
1270
+ onClose();
1271
+ active.run();
1272
+ }
1273
+ };
1274
+ return createPortal2(
1275
+ /* @__PURE__ */ jsx9(
1276
+ "div",
1277
+ {
1278
+ className: "mst-overlay",
1279
+ role: "presentation",
1280
+ onMouseDown: (e) => {
1281
+ if (e.target === e.currentTarget) onClose();
1282
+ },
1283
+ children: /* @__PURE__ */ jsxs8("div", { className: "mst-palette", role: "dialog", "aria-modal": "true", "aria-label": t.commandPalette, children: [
1284
+ /* @__PURE__ */ jsxs8("div", { className: "mst-palette__input", children: [
1285
+ /* @__PURE__ */ jsx9(Icon, { name: "search", size: 15 }),
1286
+ /* @__PURE__ */ jsx9(
1287
+ "input",
1288
+ {
1289
+ ref: inputRef,
1290
+ type: "text",
1291
+ value: query,
1292
+ placeholder: t.palettePlaceholder,
1293
+ "aria-label": t.commandPalette,
1294
+ onChange: (e) => setQuery(e.target.value),
1295
+ onKeyDown
1296
+ }
1297
+ ),
1298
+ /* @__PURE__ */ jsx9("kbd", { className: "mst-kbd", children: "esc" })
1299
+ ] }),
1300
+ /* @__PURE__ */ jsx9("div", { className: "mst-palette__list", ref: listRef, children: flat.length === 0 ? /* @__PURE__ */ jsx9("p", { className: "mst-palette__empty", children: t.noMenuResults }) : visible.map((section) => /* @__PURE__ */ jsxs8("div", { children: [
1301
+ /* @__PURE__ */ jsx9("p", { className: "mst-palette__section", children: section.label }),
1302
+ section.commands.map((command) => /* @__PURE__ */ jsxs8(
1303
+ "button",
1304
+ {
1305
+ type: "button",
1306
+ className: "mst-palette__row",
1307
+ "data-active": String(command.id === active?.id),
1308
+ onMouseEnter: () => setCursor(flat.findIndex((c) => c.id === command.id)),
1309
+ onClick: () => {
1310
+ onClose();
1311
+ command.run();
1312
+ },
1313
+ children: [
1314
+ command.icon && /* @__PURE__ */ jsx9(Icon, { name: command.icon, size: 14 }),
1315
+ /* @__PURE__ */ jsx9("span", { className: "mst-palette__label", children: /* @__PURE__ */ jsx9(Highlight2, { label: command.label, query }) }),
1316
+ command.hint && /* @__PURE__ */ jsx9("span", { className: "mst-palette__hint", children: command.hint })
1317
+ ]
1318
+ },
1319
+ command.id
1320
+ ))
1321
+ ] }, section.id)) })
1322
+ ] })
1323
+ }
1324
+ ),
1325
+ root ?? document.body
1326
+ );
1327
+ }
1328
+
1329
+ // src/components/EntityList.tsx
1330
+ import { useMemo as useMemo6, useState as useState9 } from "react";
1331
+
1332
+ // src/columns.ts
1333
+ import { useCallback as useCallback3, useMemo as useMemo5, useState as useState7 } from "react";
1334
+ var STORAGE_PREFIX = "maestro-admin:columns";
1335
+ function read(key) {
1336
+ try {
1337
+ const raw = globalThis.localStorage?.getItem(key);
1338
+ const parsed = raw ? JSON.parse(raw) : void 0;
1339
+ return Array.isArray(parsed) ? parsed.filter((x) => typeof x === "string") : void 0;
1340
+ } catch {
1341
+ return void 0;
1342
+ }
1343
+ }
1344
+ function write(key, names) {
1345
+ try {
1346
+ if (names) globalThis.localStorage?.setItem(key, JSON.stringify(names));
1347
+ else globalThis.localStorage?.removeItem(key);
1348
+ } catch {
1349
+ }
1350
+ }
1351
+ function useColumnPrefs(entity, max = 6) {
1352
+ const key = `${STORAGE_PREFIX}:${entity.id}`;
1353
+ const [selection, setSelection] = useState7(() => read(key));
1354
+ const available = useMemo5(() => listFields(entity), [entity]);
1355
+ const fallback = useMemo5(() => primaryListFields(entity, max), [entity, max]);
1356
+ const columns = useMemo5(() => {
1357
+ if (!selection) return fallback;
1358
+ const chosen = available.filter((f) => selection.includes(f.name));
1359
+ return chosen.length > 0 ? chosen : fallback;
1360
+ }, [selection, available, fallback]);
1361
+ const toggle = useCallback3(
1362
+ (name) => {
1363
+ setSelection((prev) => {
1364
+ const current = prev ?? columns.map((f) => f.name);
1365
+ const next = current.includes(name) ? current.filter((n) => n !== name) : [...current, name];
1366
+ write(key, next);
1367
+ return next;
1368
+ });
1369
+ },
1370
+ [key, columns]
1371
+ );
1372
+ const reset = useCallback3(() => {
1373
+ write(key, void 0);
1374
+ setSelection(void 0);
1375
+ }, [key]);
1376
+ const visibleNames = useMemo5(() => new Set(columns.map((f) => f.name)), [columns]);
1377
+ return {
1378
+ columns,
1379
+ available,
1380
+ visible: (name) => visibleNames.has(name),
1381
+ toggle,
1382
+ reset,
1383
+ customized: selection !== void 0
1384
+ };
557
1385
  }
558
1386
 
559
1387
  // src/components/RelationValue.tsx
560
- import { useEffect as useEffect3, useState as useState3 } from "react";
561
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1388
+ import { useEffect as useEffect5, useState as useState8 } from "react";
1389
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
562
1390
  var displayCache = /* @__PURE__ */ new Map();
563
1391
  function resolveDisplay(client, entityId, displayField, id) {
564
1392
  const key = `${entityId}/${id}`;
@@ -579,8 +1407,8 @@ function RelationValue({ field, value }) {
579
1407
  const client = useClient();
580
1408
  const target = useEntity(field.relationEntity);
581
1409
  const id = String(value);
582
- const [display, setDisplay] = useState3(void 0);
583
- useEffect3(() => {
1410
+ const [display, setDisplay] = useState8(void 0);
1411
+ useEffect5(() => {
584
1412
  let cancelled = false;
585
1413
  setDisplay(void 0);
586
1414
  if (!target || target.displayField === target.primaryKey) return;
@@ -591,24 +1419,17 @@ function RelationValue({ field, value }) {
591
1419
  cancelled = true;
592
1420
  };
593
1421
  }, [client, target, id]);
594
- return /* @__PURE__ */ jsxs3(
595
- "span",
596
- {
597
- title: id,
598
- className: "inline-flex max-w-56 items-center gap-1 truncate rounded border border-slate-200 bg-slate-50 px-1.5 py-0.5 text-xs font-medium text-slate-700",
599
- children: [
600
- target && /* @__PURE__ */ jsxs3("span", { className: "text-slate-400", children: [
601
- target.label.singular,
602
- ":"
603
- ] }),
604
- /* @__PURE__ */ jsx3("span", { className: "truncate", children: display ?? shortId(id) })
605
- ]
606
- }
607
- );
1422
+ return /* @__PURE__ */ jsxs9("span", { title: id, className: "mst-relation", children: [
1423
+ target && /* @__PURE__ */ jsxs9("span", { className: "mst-subtle", children: [
1424
+ target.label.singular,
1425
+ ":"
1426
+ ] }),
1427
+ /* @__PURE__ */ jsx10("span", { className: "mst-relation__label", children: display ?? shortId(id) })
1428
+ ] });
608
1429
  }
609
1430
 
610
1431
  // src/components/ObjectValue.tsx
611
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1432
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
612
1433
  var ISO_DATETIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
613
1434
  var MAX_DEPTH = 4;
614
1435
  function isPlainObject(value) {
@@ -616,49 +1437,40 @@ function isPlainObject(value) {
616
1437
  }
617
1438
  function Primitive({ value }) {
618
1439
  if (value === null || value === void 0) {
619
- return /* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "\u2014" });
1440
+ return /* @__PURE__ */ jsx11("span", { className: "mst-subtle", children: "\u2014" });
620
1441
  }
621
1442
  if (typeof value === "boolean" || typeof value === "number") {
622
- return /* @__PURE__ */ jsx4("code", { className: "text-xs font-medium text-indigo-700", children: String(value) });
1443
+ return /* @__PURE__ */ jsx11("code", { className: "mst-mono", style: { color: "var(--maestro-brand)" }, children: String(value) });
623
1444
  }
624
1445
  const text = String(value);
625
1446
  if (ISO_DATETIME.test(text)) {
626
1447
  const d = new Date(text);
627
1448
  if (!Number.isNaN(d.getTime())) {
628
- return /* @__PURE__ */ jsx4("span", { className: "whitespace-nowrap text-slate-700", children: d.toLocaleString() });
1449
+ return /* @__PURE__ */ jsx11("span", { className: "mst-nums", children: d.toLocaleString() });
629
1450
  }
630
1451
  }
631
- return /* @__PURE__ */ jsx4("span", { className: "break-all text-slate-700", children: text });
1452
+ return /* @__PURE__ */ jsx11("span", { style: { overflowWrap: "anywhere" }, children: text });
632
1453
  }
633
1454
  function ObjectValue({ value, depth = 0 }) {
634
1455
  const t = useT();
635
1456
  if (!isPlainObject(value) && !Array.isArray(value)) {
636
- return /* @__PURE__ */ jsx4(Primitive, { value });
1457
+ return /* @__PURE__ */ jsx11(Primitive, { value });
637
1458
  }
638
1459
  if (depth >= MAX_DEPTH) {
639
- return /* @__PURE__ */ jsx4("code", { className: "block max-w-md overflow-x-auto rounded bg-slate-50 px-1.5 py-0.5 text-xs text-slate-700", children: JSON.stringify(value) });
1460
+ return /* @__PURE__ */ jsx11("code", { className: "mst-code mst-code--block", children: JSON.stringify(value) });
640
1461
  }
641
1462
  const entries = Array.isArray(value) ? value.map((v, i) => [String(i), v]) : Object.entries(value);
642
1463
  if (entries.length === 0) {
643
- return /* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: t.emptyObject });
1464
+ return /* @__PURE__ */ jsx11("span", { className: "mst-subtle", children: t.emptyObject });
644
1465
  }
645
- return /* @__PURE__ */ jsx4(
646
- "dl",
647
- {
648
- className: depth === 0 ? "w-full divide-y divide-slate-100 rounded-md border border-slate-200 bg-slate-50/50" : "w-full divide-y divide-slate-100 border-l-2 border-slate-200 pl-2",
649
- children: entries.map(([key, entry]) => (
650
- // flex-wrap + basis: deeply nested values drop to their own full-width line instead of squeezing.
651
- /* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-start gap-x-3 gap-y-1 px-3 py-1.5", children: [
652
- /* @__PURE__ */ jsx4("dt", { className: "w-36 shrink-0 truncate pt-px text-xs font-medium text-slate-500", title: key, children: key }),
653
- /* @__PURE__ */ jsx4("dd", { className: "min-w-0 flex-1 basis-56 text-sm", children: isPlainObject(entry) || Array.isArray(entry) ? /* @__PURE__ */ jsx4(ObjectValue, { value: entry, depth: depth + 1 }) : /* @__PURE__ */ jsx4(Primitive, { value: entry }) })
654
- ] }, key)
655
- ))
656
- }
657
- );
1466
+ return /* @__PURE__ */ jsx11("dl", { className: depth === 0 ? "mst-objectvalue mst-objectvalue--root" : "mst-objectvalue mst-objectvalue--nested", children: entries.map(([key, entry]) => /* @__PURE__ */ jsxs10("div", { className: "mst-objectvalue__row", children: [
1467
+ /* @__PURE__ */ jsx11("dt", { className: "mst-objectvalue__key", title: key, children: key }),
1468
+ /* @__PURE__ */ jsx11("dd", { className: "mst-objectvalue__val", children: isPlainObject(entry) || Array.isArray(entry) ? /* @__PURE__ */ jsx11(ObjectValue, { value: entry, depth: depth + 1 }) : /* @__PURE__ */ jsx11(Primitive, { value: entry }) })
1469
+ ] }, key)) });
658
1470
  }
659
1471
 
660
1472
  // src/components/FieldValue.tsx
661
- import { jsx as jsx5 } from "react/jsx-runtime";
1473
+ import { jsx as jsx12 } from "react/jsx-runtime";
662
1474
  function enumLabel(field, value) {
663
1475
  const opt = field.enumOptions?.find((o) => o.value === value);
664
1476
  return opt ? opt.label : String(value);
@@ -666,117 +1478,42 @@ function enumLabel(field, value) {
666
1478
  function FieldValue({ field, value, compact = false }) {
667
1479
  const t = useT();
668
1480
  if (value === null || value === void 0 || value === "") {
669
- return /* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "\u2014" });
1481
+ return /* @__PURE__ */ jsx12("span", { className: "mst-subtle", children: "\u2014" });
670
1482
  }
671
1483
  if (field.sensitive) {
672
- return /* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "\u2022\u2022\u2022\u2022\u2022\u2022" });
1484
+ return /* @__PURE__ */ jsx12("span", { className: "mst-subtle", children: "\u2022\u2022\u2022\u2022\u2022\u2022" });
673
1485
  }
674
1486
  switch (field.type) {
675
1487
  case "boolean":
676
- return value ? /* @__PURE__ */ jsx5(Badge, { tone: "green", children: t.yes }) : /* @__PURE__ */ jsx5(Badge, { tone: "slate", children: t.no });
1488
+ return value ? /* @__PURE__ */ jsx12(Badge, { tone: "ok", dot: true, children: t.yes }) : /* @__PURE__ */ jsx12(Badge, { dot: true, children: t.no });
677
1489
  case "enum":
678
- return /* @__PURE__ */ jsx5(Badge, { tone: "indigo", children: enumLabel(field, value) });
1490
+ return /* @__PURE__ */ jsx12(Badge, { tone: "brand", children: enumLabel(field, value) });
679
1491
  case "relation":
680
- return field.relationEntity ? /* @__PURE__ */ jsx5(RelationValue, { field, value }) : /* @__PURE__ */ jsx5("span", { className: "text-slate-700", children: String(value) });
1492
+ return field.relationEntity ? /* @__PURE__ */ jsx12(RelationValue, { field, value }) : /* @__PURE__ */ jsx12("span", { children: String(value) });
681
1493
  case "date":
682
1494
  case "datetime": {
683
1495
  const d = new Date(String(value));
684
- return /* @__PURE__ */ jsx5("span", { className: "whitespace-nowrap", children: Number.isNaN(d.getTime()) ? String(value) : d.toLocaleString() });
1496
+ return /* @__PURE__ */ jsx12("span", { className: "mst-nums", children: Number.isNaN(d.getTime()) ? String(value) : d.toLocaleString() });
685
1497
  }
1498
+ case "integer":
1499
+ case "float":
1500
+ case "number":
1501
+ case "decimal":
1502
+ case "currency":
1503
+ return /* @__PURE__ */ jsx12("span", { className: "mst-nums", children: String(value) });
686
1504
  case "json":
687
1505
  if (compact) {
688
- return /* @__PURE__ */ jsx5("code", { className: "block max-w-xs truncate overflow-x-auto rounded bg-slate-50 px-1.5 py-0.5 text-xs text-slate-700", children: JSON.stringify(value) });
1506
+ return /* @__PURE__ */ jsx12("code", { className: "mst-code", children: JSON.stringify(value) });
689
1507
  }
690
1508
  if (typeof value === "object") {
691
- return /* @__PURE__ */ jsx5(ObjectValue, { value });
1509
+ return /* @__PURE__ */ jsx12(ObjectValue, { value });
692
1510
  }
693
- return /* @__PURE__ */ jsx5("code", { className: "block max-w-md overflow-x-auto rounded bg-slate-50 px-1.5 py-0.5 text-xs text-slate-700", children: JSON.stringify(value) });
1511
+ return /* @__PURE__ */ jsx12("code", { className: "mst-code mst-code--block", children: JSON.stringify(value) });
694
1512
  default:
695
- return /* @__PURE__ */ jsx5("span", { className: `text-slate-700 ${compact ? "block max-w-64 truncate" : ""}`, children: String(value) });
1513
+ return /* @__PURE__ */ jsx12("span", { children: String(value) });
696
1514
  }
697
1515
  }
698
1516
 
699
- // src/components/menu.tsx
700
- import { useEffect as useEffect4, useLayoutEffect, useRef as useRef2, useState as useState4 } from "react";
701
- import { createPortal } from "react-dom";
702
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
703
- function PopMenu({ anchor, items, onClose }) {
704
- const ref = useRef2(null);
705
- const [pos, setPos] = useState4(anchor);
706
- useLayoutEffect(() => {
707
- const el = ref.current;
708
- if (!el) return;
709
- const rect = el.getBoundingClientRect();
710
- setPos({
711
- x: Math.max(4, Math.min(anchor.x, window.innerWidth - rect.width - 8)),
712
- y: Math.max(4, Math.min(anchor.y, window.innerHeight - rect.height - 8))
713
- });
714
- }, [anchor]);
715
- useEffect4(() => {
716
- const onDown = (e) => {
717
- if (ref.current && !ref.current.contains(e.target)) onClose();
718
- };
719
- const onKey = (e) => {
720
- if (e.key === "Escape") onClose();
721
- };
722
- document.addEventListener("mousedown", onDown);
723
- document.addEventListener("keydown", onKey);
724
- window.addEventListener("scroll", onClose, true);
725
- window.addEventListener("resize", onClose);
726
- return () => {
727
- document.removeEventListener("mousedown", onDown);
728
- document.removeEventListener("keydown", onKey);
729
- window.removeEventListener("scroll", onClose, true);
730
- window.removeEventListener("resize", onClose);
731
- };
732
- }, [onClose]);
733
- return createPortal(
734
- /* @__PURE__ */ jsx6(
735
- "div",
736
- {
737
- ref,
738
- role: "menu",
739
- style: { position: "fixed", left: pos.x, top: pos.y, zIndex: 50 },
740
- className: "min-w-44 rounded-md border border-slate-200 bg-white py-1 shadow-lg",
741
- children: items.map((item) => /* @__PURE__ */ jsx6(
742
- "button",
743
- {
744
- role: "menuitem",
745
- onClick: () => {
746
- onClose();
747
- item.onSelect();
748
- },
749
- className: `block w-full px-3 py-1.5 text-left text-sm ${item.danger ? "text-red-600 hover:bg-red-50" : "text-slate-700 hover:bg-slate-100"}`,
750
- children: item.label
751
- },
752
- item.id
753
- ))
754
- }
755
- ),
756
- document.body
757
- );
758
- }
759
- function MenuButton({ items, label }) {
760
- const [anchor, setAnchor] = useState4(void 0);
761
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
762
- /* @__PURE__ */ jsx6(
763
- "button",
764
- {
765
- type: "button",
766
- "aria-label": label,
767
- title: label,
768
- onClick: (e) => {
769
- const rect = e.currentTarget.getBoundingClientRect();
770
- setAnchor({ x: rect.right - 176, y: rect.bottom + 4 });
771
- },
772
- className: "rounded-md px-2 py-1 text-slate-500 hover:bg-slate-100 hover:text-slate-900",
773
- children: "\u22EE"
774
- }
775
- ),
776
- anchor && /* @__PURE__ */ jsx6(PopMenu, { anchor, items, onClose: () => setAnchor(void 0) })
777
- ] });
778
- }
779
-
780
1517
  // src/clipboard.ts
781
1518
  async function copyText(text) {
782
1519
  if (navigator.clipboard?.writeText) {
@@ -797,8 +1534,9 @@ async function copyText(text) {
797
1534
  }
798
1535
 
799
1536
  // src/components/EntityList.tsx
800
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1537
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
801
1538
  var PAGE_SIZE = 20;
1539
+ var ACTIONS_COLUMN_WIDTH = 44;
802
1540
  function filterFor(field, value) {
803
1541
  if (value === null || value === void 0) return { field: field.name, operator: "isNull" };
804
1542
  if (field.type === "boolean") return { field: field.name, operator: value ? "isTrue" : "isFalse" };
@@ -813,6 +1551,11 @@ function filterChipText(entity, filter) {
813
1551
  const value = filter.value ?? "";
814
1552
  return `${label} = ${value.length > 24 ? `${value.slice(0, 24)}\u2026` : value}`;
815
1553
  }
1554
+ function pageWindow(page, totalPages) {
1555
+ const size = Math.min(5, totalPages);
1556
+ const start = Math.max(1, Math.min(page - 2, totalPages - size + 1));
1557
+ return Array.from({ length: size }, (_, i) => start + i);
1558
+ }
816
1559
  function EntityList({
817
1560
  entity,
818
1561
  onOpen,
@@ -826,14 +1569,16 @@ function EntityList({
826
1569
  const t = useT();
827
1570
  const entities = useEntities();
828
1571
  const relatedLabel = (entityId) => entities.find((e) => e.id === entityId)?.label.singular ?? entityId;
829
- const columns = useMemo2(() => primaryListFields(entity), [entity]);
830
- const searchFields = useMemo2(() => entity.fields.filter((f) => f.searchable).map((f) => f.name), [entity]);
831
- const [page, setPage] = useState5(1);
832
- const [search, setSearch] = useState5("");
833
- const [sort, setSort] = useState5(void 0);
834
- const [filters, setFilters] = useState5(initialFilters ?? []);
835
- const [cellMenu, setCellMenu] = useState5(void 0);
836
- const [copied, setCopied] = useState5(false);
1572
+ const columnPrefs = useColumnPrefs(entity);
1573
+ const columns = columnPrefs.columns;
1574
+ const searchFields = useMemo6(() => entity.fields.filter((f) => f.searchable).map((f) => f.name), [entity]);
1575
+ const [page, setPage] = useState9(1);
1576
+ const [search, setSearch] = useState9("");
1577
+ const [sort, setSort] = useState9(void 0);
1578
+ const [filters, setFilters] = useState9(initialFilters ?? []);
1579
+ const [cellMenu, setCellMenu] = useState9(void 0);
1580
+ const [columnMenu, setColumnMenu] = useState9(void 0);
1581
+ const [copied, setCopied] = useState9(false);
837
1582
  const query = {
838
1583
  page,
839
1584
  pageSize: PAGE_SIZE,
@@ -870,17 +1615,31 @@ function EntityList({
870
1615
  };
871
1616
  const rowMenuItems = (record) => {
872
1617
  const id = String(record[entity.primaryKey]);
873
- const items = [{ id: "view", label: `\u{1F441} ${t.view}`, onSelect: () => onOpen(id) }];
874
- if (entity.capabilities.update) items.push({ id: "edit", label: `\u270F\uFE0F ${t.edit}`, onSelect: () => onEdit(id) });
1618
+ const items = [{ id: "view", label: t.view, icon: "eye", onSelect: () => onOpen(id) }];
1619
+ if (entity.capabilities.update) {
1620
+ items.push({ id: "edit", label: t.edit, icon: "pencil", onSelect: () => onEdit(id) });
1621
+ }
875
1622
  if (entity.capabilities.clone && entity.capabilities.create) {
876
- items.push({ id: "clone", label: `\u29C9 ${t.clone}`, onSelect: () => onClone(id) });
1623
+ items.push({ id: "clone", label: t.clone, icon: "clone", onSelect: () => onClone(id) });
877
1624
  }
878
1625
  items.push(
879
- { id: "copy-id", label: `\u2398 ${t.copyId}`, onSelect: () => void copy(id) },
880
- { id: "copy-object", label: `{} ${t.copyObject}`, onSelect: () => void copy(JSON.stringify(record, null, 2)) }
1626
+ { id: "copy-id", label: t.copyId, icon: "copy", separated: true, onSelect: () => void copy(id) },
1627
+ {
1628
+ id: "copy-object",
1629
+ label: t.copyObject,
1630
+ icon: "copy",
1631
+ onSelect: () => void copy(JSON.stringify(record, null, 2))
1632
+ }
881
1633
  );
882
1634
  if (entity.capabilities.delete) {
883
- items.push({ id: "remove", label: `\u{1F5D1} ${t.remove}`, danger: true, onSelect: () => void onDelete(id) });
1635
+ items.push({
1636
+ id: "remove",
1637
+ label: t.remove,
1638
+ icon: "trash",
1639
+ danger: true,
1640
+ separated: true,
1641
+ onSelect: () => void onDelete(id)
1642
+ });
884
1643
  }
885
1644
  return items;
886
1645
  };
@@ -890,130 +1649,195 @@ function EntityList({
890
1649
  const value = record[field.name];
891
1650
  const items = [];
892
1651
  const filter = filterFor(field, value);
893
- if (filter) items.push({ id: "filter", label: `\u25BC ${t.addToFilter}`, onSelect: () => addFilter(filter) });
894
- items.push({ id: "view", label: `\u{1F441} ${t.view}`, onSelect: () => onOpen(id) });
895
- if (entity.capabilities.update) items.push({ id: "edit", label: `\u270F\uFE0F ${t.edit}`, onSelect: () => onEdit(id) });
1652
+ if (filter) items.push({ id: "filter", label: t.addToFilter, icon: "filter", onSelect: () => addFilter(filter) });
1653
+ items.push({ id: "view", label: t.view, icon: "eye", onSelect: () => onOpen(id) });
1654
+ if (entity.capabilities.update) items.push({ id: "edit", label: t.edit, icon: "pencil", onSelect: () => onEdit(id) });
896
1655
  if (field.type === "relation" && field.relationEntity && value !== null && value !== void 0) {
897
1656
  const targetId = field.relationEntity;
898
1657
  items.push({
899
1658
  id: "open-related",
900
- label: `\u2197 ${t.openRelated(relatedLabel(targetId))}`,
1659
+ label: t.openRelated(relatedLabel(targetId)),
1660
+ icon: "arrowUpRight",
901
1661
  onSelect: () => onNavigate(targetId, String(value))
902
1662
  });
903
1663
  }
904
1664
  if (value !== null && value !== void 0) {
905
1665
  items.push({
906
1666
  id: "copy-value",
907
- label: `\u2398 ${t.copyValue}`,
1667
+ label: t.copyValue,
1668
+ icon: "copy",
1669
+ separated: true,
908
1670
  onSelect: () => void copy(typeof value === "object" ? JSON.stringify(value) : String(value))
909
1671
  });
910
1672
  }
911
1673
  return items;
912
1674
  };
1675
+ const columnMenuItems = [
1676
+ ...columnPrefs.available.map((field) => ({
1677
+ id: `col:${field.name}`,
1678
+ label: field.label,
1679
+ icon: columnPrefs.visible(field.name) ? "check" : void 0,
1680
+ onSelect: () => columnPrefs.toggle(field.name)
1681
+ })),
1682
+ ...columnPrefs.customized ? [{ id: "reset", label: t.resetColumns, icon: "rows", separated: true, onSelect: columnPrefs.reset }] : []
1683
+ ];
913
1684
  const totalPages = data?.totalPages ?? (data ? Math.max(1, Math.ceil(data.total / PAGE_SIZE)) : 1);
914
- return /* @__PURE__ */ jsxs6("section", { className: "space-y-4", children: [
915
- /* @__PURE__ */ jsxs6("header", { className: "flex flex-wrap items-center justify-between gap-3", children: [
916
- /* @__PURE__ */ jsxs6("div", { children: [
917
- /* @__PURE__ */ jsx7("h1", { className: "text-lg font-semibold text-slate-900", children: entity.label.plural }),
918
- data && /* @__PURE__ */ jsx7("p", { className: "text-sm text-slate-500", children: t.records(data.total) })
1685
+ const firstRow = data && data.total > 0 ? (page - 1) * PAGE_SIZE + 1 : 0;
1686
+ const lastRow = data ? Math.min(page * PAGE_SIZE, data.total) : 0;
1687
+ return /* @__PURE__ */ jsxs11("section", { className: "mst-page mst-page--fill", children: [
1688
+ /* @__PURE__ */ jsxs11("div", { className: "mst-pagehead", children: [
1689
+ /* @__PURE__ */ jsxs11("div", { children: [
1690
+ /* @__PURE__ */ jsx13("h1", { className: "mst-pagehead__title", children: entity.label.plural }),
1691
+ /* @__PURE__ */ jsxs11("p", { className: "mst-pagehead__meta", children: [
1692
+ data && /* @__PURE__ */ jsx13("span", { className: "mst-nums", children: t.records(data.total) }),
1693
+ /* @__PURE__ */ jsxs11("span", { className: "mst-source", children: [
1694
+ /* @__PURE__ */ jsx13(Icon, { name: "database", size: 11 }),
1695
+ /* @__PURE__ */ jsxs11("span", { children: [
1696
+ entity.datasource,
1697
+ " \xB7 ",
1698
+ entity.table
1699
+ ] })
1700
+ ] })
1701
+ ] })
919
1702
  ] }),
920
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
921
- searchFields.length > 0 && /* @__PURE__ */ jsx7(
1703
+ /* @__PURE__ */ jsxs11("div", { className: "mst-pagehead__actions", children: [
1704
+ searchFields.length > 0 && /* @__PURE__ */ jsx13(
922
1705
  "input",
923
1706
  {
924
1707
  type: "search",
1708
+ className: "mst-input mst-input--inline",
925
1709
  placeholder: t.searchPlaceholder(entity.label.plural),
926
1710
  value: search,
927
1711
  onChange: (e) => {
928
1712
  setSearch(e.target.value);
929
1713
  setPage(1);
1714
+ }
1715
+ }
1716
+ ),
1717
+ /* @__PURE__ */ jsxs11(
1718
+ Button,
1719
+ {
1720
+ icon: "columns",
1721
+ onClick: (e) => {
1722
+ const rect = e.currentTarget.getBoundingClientRect();
1723
+ setColumnMenu({ x: rect.right - 200, y: rect.bottom + 6 });
930
1724
  },
931
- className: "rounded-md border border-slate-300 px-3 py-1.5 text-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
1725
+ children: [
1726
+ t.columns,
1727
+ " ",
1728
+ /* @__PURE__ */ jsx13("span", { className: "mst-subtle mst-nums", children: t.columnsCount(columns.length, columnPrefs.available.length) })
1729
+ ]
932
1730
  }
933
1731
  ),
934
- entity.capabilities.create && /* @__PURE__ */ jsx7(Button, { variant: "primary", onClick: onCreate, children: t.newRecord })
1732
+ entity.capabilities.create && /* @__PURE__ */ jsx13(Button, { variant: "primary", icon: "plus", onClick: onCreate, children: t.newRecord })
935
1733
  ] })
936
1734
  ] }),
937
- filters.length > 0 && /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [
938
- /* @__PURE__ */ jsxs6("span", { className: "text-slate-500", children: [
939
- t.filters,
940
- ":"
941
- ] }),
942
- filters.map((f) => /* @__PURE__ */ jsxs6(
1735
+ filters.length > 0 && /* @__PURE__ */ jsxs11("div", { className: "mst-chips", children: [
1736
+ filters.map((f) => /* @__PURE__ */ jsxs11(
943
1737
  "button",
944
1738
  {
1739
+ type: "button",
1740
+ className: "mst-chip",
1741
+ title: f.value,
945
1742
  onClick: () => {
946
1743
  setFilters((prev) => prev.filter((p) => p.field !== f.field));
947
1744
  setPage(1);
948
1745
  },
949
- title: f.value,
950
- className: "inline-flex items-center gap-1 rounded-full bg-indigo-50 px-2.5 py-0.5 text-xs font-medium text-indigo-700 hover:bg-indigo-100",
951
1746
  children: [
952
1747
  filterChipText(entity, f),
953
- /* @__PURE__ */ jsx7("span", { "aria-hidden": true, children: "\xD7" })
1748
+ /* @__PURE__ */ jsx13(Icon, { name: "close", size: 11 })
954
1749
  ]
955
1750
  },
956
1751
  f.field
957
1752
  )),
958
- /* @__PURE__ */ jsx7(
1753
+ /* @__PURE__ */ jsx13(
959
1754
  "button",
960
1755
  {
1756
+ type: "button",
1757
+ className: "mst-btn mst-btn--ghost",
961
1758
  onClick: () => {
962
1759
  setFilters([]);
963
1760
  setPage(1);
964
1761
  },
965
- className: "text-xs text-slate-500 underline hover:text-slate-700",
966
1762
  children: t.clearFilters
967
1763
  }
968
1764
  )
969
1765
  ] }),
970
- error && /* @__PURE__ */ jsx7(ErrorBanner, { error }),
971
- loading && !data ? /* @__PURE__ */ jsx7(Spinner, { label: t.loading }) : data && data.records.length === 0 ? /* @__PURE__ */ jsx7(EmptyState, { title: t.noRecords, hint: search || filters.length ? t.tryDifferentSearch : void 0 }) : /* @__PURE__ */ jsx7("div", { className: "overflow-x-auto rounded-lg border border-slate-200", children: /* @__PURE__ */ jsxs6("table", { className: "min-w-full divide-y divide-slate-200 text-sm", children: [
972
- /* @__PURE__ */ jsx7("thead", { className: "bg-slate-50", children: /* @__PURE__ */ jsxs6("tr", { children: [
973
- columns.map((f) => /* @__PURE__ */ jsxs6(
974
- "th",
975
- {
976
- onClick: () => f.sortable && toggleSort(f.name),
977
- className: `px-3 py-2 text-left font-medium text-slate-600 ${f.sortable ? "cursor-pointer select-none hover:text-slate-900" : ""}`,
978
- children: [
979
- f.label,
980
- sort?.field === f.name && (sort.direction === "asc" ? " \u25B2" : " \u25BC")
981
- ]
982
- },
983
- f.name
984
- )),
985
- /* @__PURE__ */ jsx7("th", { className: "px-3 py-2 text-right font-medium text-slate-600", children: t.actions })
986
- ] }) }),
987
- /* @__PURE__ */ jsx7("tbody", { className: "divide-y divide-slate-100 bg-white", children: data?.records.map((record, i) => {
988
- const id = String(record[entity.primaryKey]);
989
- return /* @__PURE__ */ jsxs6("tr", { className: "hover:bg-slate-50", children: [
990
- columns.map((f) => /* @__PURE__ */ jsx7(
991
- "td",
1766
+ error && /* @__PURE__ */ jsx13(ErrorBanner, { error }),
1767
+ loading && !data ? /* @__PURE__ */ jsx13(Spinner, { label: t.loading }) : data && data.records.length === 0 ? /* @__PURE__ */ jsx13(EmptyState, { title: t.noRecords, hint: search || filters.length ? t.tryDifferentSearch : void 0 }) : /* @__PURE__ */ jsxs11("div", { className: "mst-card mst-tablewrap", children: [
1768
+ /* @__PURE__ */ jsx13("div", { className: "mst-tablescroll", children: /* @__PURE__ */ jsxs11("table", { className: "mst-table", children: [
1769
+ /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsxs11("tr", { children: [
1770
+ columns.map((f) => /* @__PURE__ */ jsx13(
1771
+ "th",
992
1772
  {
993
- className: "cursor-pointer px-3 py-2 align-top",
994
- onClick: (e) => setCellMenu({ anchor: { x: e.clientX, y: e.clientY }, field: f, record }),
995
- onDoubleClick: () => onOpen(id),
996
- children: /* @__PURE__ */ jsx7(FieldValue, { field: f, value: record[f.name], compact: true })
1773
+ style: { width: f.list.width, textAlign: f.list.align },
1774
+ "aria-sort": sort?.field === f.name ? sort.direction === "asc" ? "ascending" : "descending" : "none",
1775
+ children: f.sortable ? /* @__PURE__ */ jsxs11("button", { type: "button", className: "mst-th__button", onClick: () => toggleSort(f.name), children: [
1776
+ /* @__PURE__ */ jsx13("span", { className: "mst-th__label", children: f.label }),
1777
+ /* @__PURE__ */ jsx13(
1778
+ Icon,
1779
+ {
1780
+ name: sort?.field === f.name ? sort.direction === "asc" ? "sortAsc" : "sortDesc" : "sort",
1781
+ size: 11
1782
+ }
1783
+ )
1784
+ ] }) : /* @__PURE__ */ jsx13("span", { className: "mst-th__label", children: f.label })
997
1785
  },
998
1786
  f.name
999
1787
  )),
1000
- /* @__PURE__ */ jsx7("td", { className: "whitespace-nowrap px-3 py-2 text-right", children: /* @__PURE__ */ jsx7(MenuButton, { label: t.actions, items: rowMenuItems(record) }) })
1001
- ] }, id || i);
1002
- }) })
1003
- ] }) }),
1004
- cellMenu && /* @__PURE__ */ jsx7(PopMenu, { anchor: cellMenu.anchor, items: cellMenuItems(cellMenu), onClose: () => setCellMenu(void 0) }),
1005
- copied && /* @__PURE__ */ jsx7("div", { className: "fixed bottom-4 right-4 z-50 rounded-md bg-slate-900 px-3 py-1.5 text-sm text-white shadow-lg", children: t.copied }),
1006
- data && totalPages > 1 && /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-2 text-sm", children: [
1007
- /* @__PURE__ */ jsx7(Button, { variant: "secondary", disabled: page <= 1, onClick: () => setPage((p) => p - 1), children: t.previous }),
1008
- /* @__PURE__ */ jsx7("span", { className: "text-slate-500", children: t.page(page, totalPages) }),
1009
- /* @__PURE__ */ jsx7(Button, { variant: "secondary", disabled: page >= totalPages, onClick: () => setPage((p) => p + 1), children: t.next })
1010
- ] })
1788
+ /* @__PURE__ */ jsx13("th", { style: { width: ACTIONS_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx13("span", { className: "mst-sr-only", children: t.actions }) })
1789
+ ] }) }),
1790
+ /* @__PURE__ */ jsx13("tbody", { children: data?.records.map((record, i) => {
1791
+ const id = String(record[entity.primaryKey]);
1792
+ return /* @__PURE__ */ jsxs11("tr", { children: [
1793
+ columns.map((f) => /* @__PURE__ */ jsx13(
1794
+ "td",
1795
+ {
1796
+ className: "mst-table__cell",
1797
+ style: { textAlign: f.list.align },
1798
+ title: f.label,
1799
+ onClick: (e) => setCellMenu({ anchor: { x: e.clientX, y: e.clientY }, field: f, record }),
1800
+ onDoubleClick: () => onOpen(id),
1801
+ children: /* @__PURE__ */ jsx13(FieldValue, { field: f, value: record[f.name], compact: true })
1802
+ },
1803
+ f.name
1804
+ )),
1805
+ /* @__PURE__ */ jsx13("td", { className: "mst-td--actions", children: /* @__PURE__ */ jsx13(MenuButton, { label: t.actions, items: rowMenuItems(record) }) })
1806
+ ] }, id || i);
1807
+ }) })
1808
+ ] }) }),
1809
+ data && /* @__PURE__ */ jsxs11("div", { className: "mst-pagination", children: [
1810
+ /* @__PURE__ */ jsxs11("span", { className: "mst-nums", children: [
1811
+ firstRow,
1812
+ "\u2013",
1813
+ lastRow
1814
+ ] }),
1815
+ /* @__PURE__ */ jsx13("span", { children: t.records(data.total) }),
1816
+ totalPages > 1 && /* @__PURE__ */ jsxs11("span", { className: "mst-pagination__nums", children: [
1817
+ /* @__PURE__ */ jsx13("button", { type: "button", "aria-label": t.previous, disabled: page <= 1, onClick: () => setPage((p) => p - 1), children: /* @__PURE__ */ jsx13(Icon, { name: "chevronLeft", size: 13 }) }),
1818
+ pageWindow(page, totalPages).map((p) => /* @__PURE__ */ jsx13("button", { type: "button", "aria-current": p === page, onClick: () => setPage(p), children: p }, p)),
1819
+ /* @__PURE__ */ jsx13(
1820
+ "button",
1821
+ {
1822
+ type: "button",
1823
+ "aria-label": t.next,
1824
+ disabled: page >= totalPages,
1825
+ onClick: () => setPage((p) => p + 1),
1826
+ children: /* @__PURE__ */ jsx13(Icon, { name: "chevronRight", size: 13 })
1827
+ }
1828
+ )
1829
+ ] })
1830
+ ] })
1831
+ ] }),
1832
+ cellMenu && /* @__PURE__ */ jsx13(PopMenu, { anchor: cellMenu.anchor, items: cellMenuItems(cellMenu), onClose: () => setCellMenu(void 0) }),
1833
+ columnMenu && /* @__PURE__ */ jsx13(PopMenu, { anchor: columnMenu, items: columnMenuItems, onClose: () => setColumnMenu(void 0) }),
1834
+ copied && /* @__PURE__ */ jsx13(Toast, { children: t.copied })
1011
1835
  ] });
1012
1836
  }
1013
1837
 
1014
1838
  // src/components/EntityDetail.tsx
1015
- import { useState as useState6 } from "react";
1016
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1839
+ import { useState as useState10 } from "react";
1840
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1017
1841
  function EntityDetail({
1018
1842
  entity,
1019
1843
  id,
@@ -1028,7 +1852,7 @@ function EntityDetail({
1028
1852
  const entities = useEntities();
1029
1853
  const { data, error, loading } = useEntityRecord(entity.id, id);
1030
1854
  const fields = detailFields(entity);
1031
- const [copied, setCopied] = useState6(false);
1855
+ const [copied, setCopied] = useState10(false);
1032
1856
  const copy = async (text) => {
1033
1857
  await copyText(text);
1034
1858
  setCopied(true);
@@ -1041,71 +1865,72 @@ function EntityDetail({
1041
1865
  };
1042
1866
  const menuItems = [];
1043
1867
  if (entity.capabilities.clone && entity.capabilities.create) {
1044
- menuItems.push({ id: "clone", label: `\u29C9 ${t.clone}`, onSelect: onClone });
1868
+ menuItems.push({ id: "clone", label: t.clone, icon: "clone", onSelect: onClone });
1045
1869
  }
1046
- menuItems.push({ id: "copy-id", label: `\u2398 ${t.copyId}`, onSelect: () => void copy(id) });
1870
+ menuItems.push({ id: "copy-id", label: t.copyId, icon: "copy", onSelect: () => void copy(id) });
1047
1871
  if (data) {
1048
1872
  menuItems.push({
1049
1873
  id: "copy-object",
1050
- label: `{} ${t.copyObject}`,
1874
+ label: t.copyObject,
1875
+ icon: "copy",
1051
1876
  onSelect: () => void copy(JSON.stringify(data, null, 2))
1052
1877
  });
1053
1878
  }
1054
1879
  if (entity.capabilities.delete) {
1055
- menuItems.push({ id: "remove", label: `\u{1F5D1} ${t.remove}`, danger: true, onSelect: () => void onDelete() });
1880
+ menuItems.push({
1881
+ id: "remove",
1882
+ label: t.remove,
1883
+ icon: "trash",
1884
+ danger: true,
1885
+ separated: true,
1886
+ onSelect: () => void onDelete()
1887
+ });
1056
1888
  }
1057
- return /* @__PURE__ */ jsxs7("section", { className: "space-y-4", children: [
1058
- /* @__PURE__ */ jsxs7("header", { className: "flex items-center justify-between gap-3", children: [
1059
- /* @__PURE__ */ jsxs7("div", { children: [
1060
- /* @__PURE__ */ jsxs7("button", { onClick: onBack, className: "text-sm text-indigo-600 hover:underline", children: [
1061
- "\u2190 ",
1889
+ return /* @__PURE__ */ jsxs12("section", { className: "mst-page", children: [
1890
+ /* @__PURE__ */ jsxs12("div", { className: "mst-pagehead", children: [
1891
+ /* @__PURE__ */ jsxs12("div", { children: [
1892
+ /* @__PURE__ */ jsxs12("button", { type: "button", className: "mst-backlink", onClick: onBack, children: [
1893
+ /* @__PURE__ */ jsx14(Icon, { name: "arrowLeft", size: 12 }),
1062
1894
  entity.label.plural
1063
1895
  ] }),
1064
- /* @__PURE__ */ jsxs7("h1", { className: "text-lg font-semibold text-slate-900", children: [
1896
+ /* @__PURE__ */ jsxs12("h1", { className: "mst-pagehead__title", children: [
1065
1897
  entity.label.singular,
1066
1898
  " ",
1067
1899
  data ? String(data[entity.displayField] ?? id) : ""
1068
1900
  ] })
1069
1901
  ] }),
1070
- /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
1071
- entity.capabilities.update && /* @__PURE__ */ jsx8(Button, { variant: "primary", onClick: onEdit, children: t.edit }),
1072
- /* @__PURE__ */ jsx8(MenuButton, { label: t.actions, items: menuItems })
1902
+ /* @__PURE__ */ jsxs12("div", { className: "mst-pagehead__actions", children: [
1903
+ entity.capabilities.update && /* @__PURE__ */ jsx14(Button, { variant: "primary", icon: "pencil", onClick: onEdit, children: t.edit }),
1904
+ /* @__PURE__ */ jsx14(MenuButton, { label: t.actions, items: menuItems })
1073
1905
  ] })
1074
1906
  ] }),
1075
- error && /* @__PURE__ */ jsx8(ErrorBanner, { error }),
1076
- loading && !data ? /* @__PURE__ */ jsx8(Spinner, {}) : data && /* @__PURE__ */ jsx8("dl", { className: "divide-y divide-slate-100 rounded-lg border border-slate-200 bg-white", children: fields.map((f) => {
1907
+ error && /* @__PURE__ */ jsx14(ErrorBanner, { error }),
1908
+ loading && !data ? /* @__PURE__ */ jsx14(Spinner, {}) : data && /* @__PURE__ */ jsx14("dl", { className: "mst-fields", children: fields.map((f) => {
1077
1909
  const value = data[f.name];
1078
1910
  const isRelation = f.type === "relation" && f.relationEntity && value !== null && value !== void 0;
1079
1911
  const target = isRelation ? entities.find((e) => e.id === f.relationEntity) : void 0;
1080
- return /* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-3 gap-4 px-4 py-3", children: [
1081
- /* @__PURE__ */ jsx8("dt", { className: "text-sm font-medium text-slate-500", children: f.label }),
1082
- /* @__PURE__ */ jsxs7("dd", { className: "col-span-2 flex items-center gap-2 text-sm", children: [
1083
- /* @__PURE__ */ jsx8(FieldValue, { field: f, value }),
1084
- isRelation && /* @__PURE__ */ jsxs7(
1085
- "button",
1086
- {
1087
- onClick: () => onNavigate(f.relationEntity, String(value)),
1088
- className: "text-xs text-indigo-600 hover:underline",
1089
- children: [
1090
- t.openRelated(target?.label.singular ?? f.relationEntity),
1091
- " \u2197"
1092
- ]
1093
- }
1094
- )
1912
+ return /* @__PURE__ */ jsxs12("div", { className: "mst-fields__row", children: [
1913
+ /* @__PURE__ */ jsx14("dt", { className: "mst-fields__key", children: f.label }),
1914
+ /* @__PURE__ */ jsxs12("dd", { className: "mst-fields__value", children: [
1915
+ /* @__PURE__ */ jsx14(FieldValue, { field: f, value }),
1916
+ isRelation && /* @__PURE__ */ jsxs12("button", { type: "button", className: "mst-backlink", onClick: () => onNavigate(f.relationEntity, String(value)), children: [
1917
+ t.openRelated(target?.label.singular ?? f.relationEntity),
1918
+ /* @__PURE__ */ jsx14(Icon, { name: "arrowUpRight", size: 11 })
1919
+ ] })
1095
1920
  ] })
1096
1921
  ] }, f.name);
1097
1922
  }) }),
1098
- copied && /* @__PURE__ */ jsx8("div", { className: "fixed bottom-4 right-4 z-50 rounded-md bg-slate-900 px-3 py-1.5 text-sm text-white shadow-lg", children: t.copied })
1923
+ copied && /* @__PURE__ */ jsx14(Toast, { children: t.copied })
1099
1924
  ] });
1100
1925
  }
1101
1926
 
1102
1927
  // src/components/EntityForm.tsx
1103
- import { useEffect as useEffect6, useState as useState8 } from "react";
1928
+ import { useEffect as useEffect7, useState as useState12 } from "react";
1104
1929
 
1105
1930
  // src/components/JsonInput.tsx
1106
- import { useEffect as useEffect5, useRef as useRef3, useState as useState7 } from "react";
1107
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1108
- var leafInputClass = "w-full rounded-md border border-slate-300 px-2.5 py-1 text-sm text-slate-800 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:bg-slate-100";
1931
+ import { useEffect as useEffect6, useRef as useRef4, useState as useState11 } from "react";
1932
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1933
+ var leafInputClass = "mst-input";
1109
1934
  var MAX_DEPTH2 = 3;
1110
1935
  function RawJsonArea({
1111
1936
  id,
@@ -1116,10 +1941,10 @@ function RawJsonArea({
1116
1941
  placeholder
1117
1942
  }) {
1118
1943
  const t = useT();
1119
- const [text, setText] = useState7(() => value === null || value === void 0 ? "" : JSON.stringify(value, null, 2));
1120
- const [invalid, setInvalid] = useState7(false);
1121
- const lastEmitted = useRef3(value);
1122
- useEffect5(() => {
1944
+ const [text, setText] = useState11(() => value === null || value === void 0 ? "" : JSON.stringify(value, null, 2));
1945
+ const [invalid, setInvalid] = useState11(false);
1946
+ const lastEmitted = useRef4(value);
1947
+ useEffect6(() => {
1123
1948
  if (value !== lastEmitted.current) {
1124
1949
  lastEmitted.current = value;
1125
1950
  setText(value === null || value === void 0 ? "" : JSON.stringify(value, null, 2));
@@ -1143,8 +1968,8 @@ function RawJsonArea({
1143
1968
  setInvalid(true);
1144
1969
  }
1145
1970
  };
1146
- return /* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
1147
- /* @__PURE__ */ jsx9(
1971
+ return /* @__PURE__ */ jsxs13("div", { className: "mst-field", children: [
1972
+ /* @__PURE__ */ jsx15(
1148
1973
  "textarea",
1149
1974
  {
1150
1975
  id,
@@ -1154,10 +1979,10 @@ function RawJsonArea({
1154
1979
  disabled,
1155
1980
  onChange: (e) => onText(e.target.value),
1156
1981
  "aria-invalid": invalid || void 0,
1157
- className: `${leafInputClass} font-mono ${invalid ? "border-amber-400 focus:border-amber-500 focus:ring-amber-500" : ""}`
1982
+ className: `mst-textarea mst-mono${invalid ? " mst-input--invalid" : ""}`
1158
1983
  }
1159
1984
  ),
1160
- invalid && /* @__PURE__ */ jsx9("p", { role: "alert", className: "text-xs text-amber-600", children: t.jsonInvalid })
1985
+ invalid && /* @__PURE__ */ jsx15("p", { role: "alert", className: "mst-help mst-help--warn", children: t.jsonInvalid })
1161
1986
  ] });
1162
1987
  }
1163
1988
  function LeafEditor({
@@ -1168,7 +1993,7 @@ function LeafEditor({
1168
1993
  depth
1169
1994
  }) {
1170
1995
  if (typeof value === "boolean") {
1171
- return /* @__PURE__ */ jsx9(
1996
+ return /* @__PURE__ */ jsx15(
1172
1997
  "input",
1173
1998
  {
1174
1999
  id,
@@ -1176,12 +2001,12 @@ function LeafEditor({
1176
2001
  checked: value,
1177
2002
  disabled,
1178
2003
  onChange: (e) => onChange(e.target.checked),
1179
- className: "mt-1 h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500"
2004
+ className: "mst-checkbox"
1180
2005
  }
1181
2006
  );
1182
2007
  }
1183
2008
  if (typeof value === "number") {
1184
- return /* @__PURE__ */ jsx9(
2009
+ return /* @__PURE__ */ jsx15(
1185
2010
  "input",
1186
2011
  {
1187
2012
  id,
@@ -1195,7 +2020,7 @@ function LeafEditor({
1195
2020
  );
1196
2021
  }
1197
2022
  if (typeof value === "string") {
1198
- return /* @__PURE__ */ jsx9(
2023
+ return /* @__PURE__ */ jsx15(
1199
2024
  "input",
1200
2025
  {
1201
2026
  id,
@@ -1208,7 +2033,7 @@ function LeafEditor({
1208
2033
  );
1209
2034
  }
1210
2035
  if (value === null || value === void 0) {
1211
- return /* @__PURE__ */ jsx9(
2036
+ return /* @__PURE__ */ jsx15(
1212
2037
  "input",
1213
2038
  {
1214
2039
  id,
@@ -1222,12 +2047,12 @@ function LeafEditor({
1222
2047
  );
1223
2048
  }
1224
2049
  if (isPlainObject(value) && depth < MAX_DEPTH2) {
1225
- return /* @__PURE__ */ jsx9("div", { className: "rounded-md border border-slate-200", children: /* @__PURE__ */ jsx9(ObjectEditor, { idPrefix: id, value, onChange, disabled, depth: depth + 1 }) });
2050
+ return /* @__PURE__ */ jsx15("div", { className: "mst-json__nest", children: /* @__PURE__ */ jsx15(ObjectEditor, { idPrefix: id, value, onChange, disabled, depth: depth + 1 }) });
1226
2051
  }
1227
2052
  if (Array.isArray(value) && depth < MAX_DEPTH2) {
1228
- return /* @__PURE__ */ jsx9("div", { className: "rounded-md border border-slate-200", children: /* @__PURE__ */ jsx9(ArrayEditor, { idPrefix: id, value, onChange, disabled, depth: depth + 1 }) });
2053
+ return /* @__PURE__ */ jsx15("div", { className: "mst-json__nest", children: /* @__PURE__ */ jsx15(ArrayEditor, { idPrefix: id, value, onChange, disabled, depth: depth + 1 }) });
1229
2054
  }
1230
- return /* @__PURE__ */ jsx9(RawJsonArea, { id, value, onChange, disabled, rows: 3 });
2055
+ return /* @__PURE__ */ jsx15(RawJsonArea, { id, value, onChange, disabled, rows: 3 });
1231
2056
  }
1232
2057
  function ArrayEditor({
1233
2058
  idPrefix,
@@ -1244,27 +2069,27 @@ function ArrayEditor({
1244
2069
  const template = last === void 0 || last === null ? null : JSON.parse(JSON.stringify(last));
1245
2070
  onChange([...value, template]);
1246
2071
  };
1247
- return /* @__PURE__ */ jsxs8("div", { className: "divide-y divide-slate-100", children: [
2072
+ return /* @__PURE__ */ jsxs13("div", { className: "mst-json__rows", children: [
1248
2073
  value.map((item, i) => {
1249
2074
  const id = `${idPrefix}-${i}`;
1250
- return /* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap items-start gap-x-3 gap-y-1.5 px-3 py-2", children: [
1251
- /* @__PURE__ */ jsx9("label", { htmlFor: id, className: "w-8 shrink-0 pt-1.5 text-right text-xs font-medium text-slate-400", children: i }),
1252
- !disabled && /* @__PURE__ */ jsx9(
2075
+ return /* @__PURE__ */ jsxs13("div", { className: "mst-json__row", children: [
2076
+ /* @__PURE__ */ jsx15("label", { htmlFor: id, className: "mst-json__index", children: i }),
2077
+ !disabled && /* @__PURE__ */ jsx15(
1253
2078
  "button",
1254
2079
  {
1255
2080
  type: "button",
1256
2081
  title: t.removeItem,
1257
2082
  "aria-label": `${t.removeItem} ${i}`,
1258
2083
  onClick: () => removeAt(i),
1259
- className: "shrink-0 pt-1.5 text-xs text-slate-400 transition-colors hover:text-red-600",
2084
+ className: "mst-json__remove",
1260
2085
  children: "\u2715"
1261
2086
  }
1262
2087
  ),
1263
- /* @__PURE__ */ jsx9("div", { className: "min-w-0 flex-1 basis-64", children: /* @__PURE__ */ jsx9(LeafEditor, { id, value: item, onChange: (next) => setAt(i, next), disabled, depth }) })
2088
+ /* @__PURE__ */ jsx15("div", { className: "mst-json__field", children: /* @__PURE__ */ jsx15(LeafEditor, { id, value: item, onChange: (next) => setAt(i, next), disabled, depth }) })
1264
2089
  ] }, i);
1265
2090
  }),
1266
- value.length === 0 && /* @__PURE__ */ jsx9("p", { className: "px-3 py-2 text-xs text-slate-400", children: t.emptyObject }),
1267
- !disabled && /* @__PURE__ */ jsx9("div", { className: "px-3 py-1.5", children: /* @__PURE__ */ jsxs8("button", { type: "button", onClick: append, className: "text-xs font-medium text-indigo-600 hover:underline", children: [
2091
+ value.length === 0 && /* @__PURE__ */ jsx15("p", { className: "mst-json__empty", children: t.emptyObject }),
2092
+ !disabled && /* @__PURE__ */ jsx15("div", { className: "mst-json__addrow", children: /* @__PURE__ */ jsxs13("button", { type: "button", onClick: append, className: "mst-backlink", children: [
1268
2093
  "+ ",
1269
2094
  t.addItem
1270
2095
  ] }) })
@@ -1280,16 +2105,16 @@ function ObjectEditor({
1280
2105
  const t = useT();
1281
2106
  const entries = Object.entries(value);
1282
2107
  if (entries.length === 0) {
1283
- return /* @__PURE__ */ jsx9("p", { className: "px-3 py-2 text-xs text-slate-400", children: t.emptyObject });
2108
+ return /* @__PURE__ */ jsx15("p", { className: "mst-json__empty", children: t.emptyObject });
1284
2109
  }
1285
- return /* @__PURE__ */ jsx9("div", { className: "divide-y divide-slate-100", children: entries.map(([key, entry]) => {
2110
+ return /* @__PURE__ */ jsx15("div", { className: "mst-json__rows", children: entries.map(([key, entry]) => {
1286
2111
  const id = `${idPrefix}-${key}`;
1287
2112
  return (
1288
2113
  // flex-wrap + basis: when the label column leaves less than ~16rem for the control (deeply
1289
2114
  // nested groups), the control wraps to its own full-width line instead of squeezing.
1290
- /* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap items-start gap-x-3 gap-y-1.5 px-3 py-2", children: [
1291
- /* @__PURE__ */ jsx9("label", { htmlFor: id, className: "w-36 shrink-0 truncate pt-1.5 text-xs font-medium text-slate-500", title: key, children: key }),
1292
- /* @__PURE__ */ jsx9("div", { className: "min-w-0 flex-1 basis-64", children: /* @__PURE__ */ jsx9(
2115
+ /* @__PURE__ */ jsxs13("div", { className: "mst-json__row", children: [
2116
+ /* @__PURE__ */ jsx15("label", { htmlFor: id, className: "mst-json__key", title: key, children: key }),
2117
+ /* @__PURE__ */ jsx15("div", { className: "mst-json__field", children: /* @__PURE__ */ jsx15(
1293
2118
  LeafEditor,
1294
2119
  {
1295
2120
  id,
@@ -1310,10 +2135,10 @@ function JsonInput({
1310
2135
  disabled
1311
2136
  }) {
1312
2137
  const t = useT();
1313
- const [mode, setMode] = useState7(() => isPlainObject(value) ? "form" : "raw");
1314
- const [modeTouched, setModeTouched] = useState7(false);
2138
+ const [mode, setMode] = useState11(() => isPlainObject(value) ? "form" : "raw");
2139
+ const [modeTouched, setModeTouched] = useState11(false);
1315
2140
  const structured = isPlainObject(value) || Array.isArray(value);
1316
- useEffect5(() => {
2141
+ useEffect6(() => {
1317
2142
  if (!modeTouched && structured) setMode("form");
1318
2143
  }, [structured, modeTouched]);
1319
2144
  const pick = (next) => {
@@ -1321,10 +2146,10 @@ function JsonInput({
1321
2146
  setMode(next);
1322
2147
  };
1323
2148
  const showForm = mode === "form" && structured;
1324
- const toggleClass = (active) => `rounded px-2 py-0.5 text-xs font-medium transition-colors ${active ? "bg-white text-slate-800 shadow-sm ring-1 ring-slate-300" : "text-slate-500 hover:text-slate-700"}`;
1325
- return /* @__PURE__ */ jsxs8("div", { className: "rounded-md border border-slate-300 shadow-sm", children: [
1326
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-end gap-1 rounded-t-md border-b border-slate-200 bg-slate-50 px-2 py-1", children: [
1327
- /* @__PURE__ */ jsx9(
2149
+ const toggleClass = (active) => `mst-json__tab${active ? " mst-json__tab--active" : ""}`;
2150
+ return /* @__PURE__ */ jsxs13("div", { className: "mst-json", children: [
2151
+ /* @__PURE__ */ jsxs13("div", { className: "mst-json__head", children: [
2152
+ /* @__PURE__ */ jsx15(
1328
2153
  "button",
1329
2154
  {
1330
2155
  type: "button",
@@ -1334,9 +2159,9 @@ function JsonInput({
1334
2159
  children: t.jsonFormMode
1335
2160
  }
1336
2161
  ),
1337
- /* @__PURE__ */ jsx9("button", { type: "button", className: toggleClass(!showForm), onClick: () => pick("raw"), children: t.jsonRawMode })
2162
+ /* @__PURE__ */ jsx15("button", { type: "button", className: toggleClass(!showForm), onClick: () => pick("raw"), children: t.jsonRawMode })
1338
2163
  ] }),
1339
- /* @__PURE__ */ jsx9("div", { className: "p-1", children: showForm ? Array.isArray(value) ? /* @__PURE__ */ jsx9(ArrayEditor, { idPrefix: `field-${field.name}`, value, onChange, disabled }) : /* @__PURE__ */ jsx9(
2164
+ /* @__PURE__ */ jsx15("div", { className: "mst-json__body", children: showForm ? Array.isArray(value) ? /* @__PURE__ */ jsx15(ArrayEditor, { idPrefix: `field-${field.name}`, value, onChange, disabled }) : /* @__PURE__ */ jsx15(
1340
2165
  ObjectEditor,
1341
2166
  {
1342
2167
  idPrefix: `field-${field.name}`,
@@ -1344,7 +2169,7 @@ function JsonInput({
1344
2169
  onChange,
1345
2170
  disabled
1346
2171
  }
1347
- ) : /* @__PURE__ */ jsx9(
2172
+ ) : /* @__PURE__ */ jsx15(
1348
2173
  RawJsonArea,
1349
2174
  {
1350
2175
  id: `field-${field.name}`,
@@ -1358,8 +2183,8 @@ function JsonInput({
1358
2183
  }
1359
2184
 
1360
2185
  // src/components/FieldInput.tsx
1361
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1362
- var inputClass = "w-full rounded-md border border-slate-300 px-3 py-1.5 text-sm text-slate-800 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 disabled:bg-slate-100";
2186
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2187
+ var inputClass = "mst-input";
1363
2188
  function FieldInput({
1364
2189
  field,
1365
2190
  value,
@@ -1369,7 +2194,7 @@ function FieldInput({
1369
2194
  const id = `field-${field.name}`;
1370
2195
  switch (field.type) {
1371
2196
  case "boolean":
1372
- return /* @__PURE__ */ jsx10(
2197
+ return /* @__PURE__ */ jsx16(
1373
2198
  "input",
1374
2199
  {
1375
2200
  id,
@@ -1377,7 +2202,7 @@ function FieldInput({
1377
2202
  checked: Boolean(value),
1378
2203
  disabled,
1379
2204
  onChange: (e) => onChange(e.target.checked),
1380
- className: "h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500"
2205
+ className: "mst-checkbox"
1381
2206
  }
1382
2207
  );
1383
2208
  case "integer":
@@ -1385,7 +2210,7 @@ function FieldInput({
1385
2210
  case "float":
1386
2211
  case "decimal":
1387
2212
  case "currency":
1388
- return /* @__PURE__ */ jsx10(
2213
+ return /* @__PURE__ */ jsx16(
1389
2214
  "input",
1390
2215
  {
1391
2216
  id,
@@ -1399,22 +2224,22 @@ function FieldInput({
1399
2224
  }
1400
2225
  );
1401
2226
  case "enum":
1402
- return /* @__PURE__ */ jsxs9(
2227
+ return /* @__PURE__ */ jsxs14(
1403
2228
  "select",
1404
2229
  {
1405
2230
  id,
1406
2231
  value: value === null || value === void 0 ? "" : String(value),
1407
2232
  disabled,
1408
2233
  onChange: (e) => onChange(e.target.value),
1409
- className: inputClass,
2234
+ className: "mst-select",
1410
2235
  children: [
1411
- /* @__PURE__ */ jsx10("option", { value: "", children: "\u2014" }),
1412
- field.enumOptions?.map((o) => /* @__PURE__ */ jsx10("option", { value: String(o.value), children: o.label }, String(o.value)))
2236
+ /* @__PURE__ */ jsx16("option", { value: "", children: "\u2014" }),
2237
+ field.enumOptions?.map((o) => /* @__PURE__ */ jsx16("option", { value: String(o.value), children: o.label }, String(o.value)))
1413
2238
  ]
1414
2239
  }
1415
2240
  );
1416
2241
  case "date":
1417
- return /* @__PURE__ */ jsx10(
2242
+ return /* @__PURE__ */ jsx16(
1418
2243
  "input",
1419
2244
  {
1420
2245
  id,
@@ -1426,9 +2251,9 @@ function FieldInput({
1426
2251
  }
1427
2252
  );
1428
2253
  case "json":
1429
- return /* @__PURE__ */ jsx10(JsonInput, { field, value, onChange, disabled });
2254
+ return /* @__PURE__ */ jsx16(JsonInput, { field, value, onChange, disabled });
1430
2255
  case "text":
1431
- return /* @__PURE__ */ jsx10(
2256
+ return /* @__PURE__ */ jsx16(
1432
2257
  "textarea",
1433
2258
  {
1434
2259
  id,
@@ -1437,11 +2262,11 @@ function FieldInput({
1437
2262
  placeholder: field.form.placeholder,
1438
2263
  disabled,
1439
2264
  onChange: (e) => onChange(e.target.value),
1440
- className: inputClass
2265
+ className: "mst-textarea"
1441
2266
  }
1442
2267
  );
1443
2268
  default:
1444
- return /* @__PURE__ */ jsx10(
2269
+ return /* @__PURE__ */ jsx16(
1445
2270
  "input",
1446
2271
  {
1447
2272
  id,
@@ -1457,7 +2282,7 @@ function FieldInput({
1457
2282
  }
1458
2283
 
1459
2284
  // src/components/EntityForm.tsx
1460
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2285
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1461
2286
  function EntityForm({
1462
2287
  entity,
1463
2288
  mode,
@@ -1471,10 +2296,10 @@ function EntityForm({
1471
2296
  const fields = formFields(entity, mode);
1472
2297
  const sourceId = mode === "edit" ? id : cloneFromId;
1473
2298
  const existing = useEntityRecord(entity.id, sourceId);
1474
- const [values, setValues] = useState8({});
1475
- const [saving, setSaving] = useState8(false);
1476
- const [error, setError] = useState8(void 0);
1477
- useEffect6(() => {
2299
+ const [values, setValues] = useState12({});
2300
+ const [saving, setSaving] = useState12(false);
2301
+ const [error, setError] = useState12(void 0);
2302
+ useEffect7(() => {
1478
2303
  if (!existing.data || !sourceId) return;
1479
2304
  if (mode === "edit") {
1480
2305
  setValues(existing.data);
@@ -1506,25 +2331,25 @@ function EntityForm({
1506
2331
  }
1507
2332
  };
1508
2333
  if (sourceId && existing.loading && !existing.data) {
1509
- return /* @__PURE__ */ jsx11(Spinner, {});
2334
+ return /* @__PURE__ */ jsx17(Spinner, {});
1510
2335
  }
1511
2336
  const title = mode === "edit" ? t.editTitle(entity.label.singular) : cloneFromId ? t.cloneTitle(entity.label.singular) : t.newTitle(entity.label.singular);
1512
- return /* @__PURE__ */ jsxs10("section", { className: "space-y-4", children: [
1513
- /* @__PURE__ */ jsxs10("header", { children: [
1514
- /* @__PURE__ */ jsxs10("button", { onClick: onCancel, className: "text-sm text-indigo-600 hover:underline", children: [
1515
- "\u2190 ",
2337
+ return /* @__PURE__ */ jsxs15("section", { className: "mst-page", children: [
2338
+ /* @__PURE__ */ jsx17("div", { className: "mst-pagehead", children: /* @__PURE__ */ jsxs15("div", { children: [
2339
+ /* @__PURE__ */ jsxs15("button", { type: "button", className: "mst-backlink", onClick: onCancel, children: [
2340
+ /* @__PURE__ */ jsx17(Icon, { name: "arrowLeft", size: 12 }),
1516
2341
  entity.label.plural
1517
2342
  ] }),
1518
- /* @__PURE__ */ jsx11("h1", { className: "text-lg font-semibold text-slate-900", children: title })
1519
- ] }),
1520
- error && /* @__PURE__ */ jsx11(ErrorBanner, { error }),
1521
- /* @__PURE__ */ jsxs10("form", { onSubmit: submit, className: "space-y-4 rounded-lg border border-slate-200 bg-white p-5", children: [
1522
- fields.map((f) => /* @__PURE__ */ jsxs10("div", { className: "space-y-1", children: [
1523
- /* @__PURE__ */ jsxs10("label", { htmlFor: `field-${f.name}`, className: "block text-sm font-medium text-slate-700", children: [
2343
+ /* @__PURE__ */ jsx17("h1", { className: "mst-pagehead__title", children: title })
2344
+ ] }) }),
2345
+ error && /* @__PURE__ */ jsx17(ErrorBanner, { error }),
2346
+ /* @__PURE__ */ jsxs15("form", { onSubmit: submit, className: "mst-card mst-form", children: [
2347
+ fields.map((f) => /* @__PURE__ */ jsxs15("div", { className: "mst-field", children: [
2348
+ /* @__PURE__ */ jsxs15("label", { htmlFor: `field-${f.name}`, className: "mst-label", children: [
1524
2349
  f.label,
1525
- f.required && /* @__PURE__ */ jsx11("span", { className: "ml-0.5 text-red-500", children: "*" })
2350
+ f.required && /* @__PURE__ */ jsx17("span", { className: "mst-label__req", children: "*" })
1526
2351
  ] }),
1527
- /* @__PURE__ */ jsx11(
2352
+ /* @__PURE__ */ jsx17(
1528
2353
  FieldInput,
1529
2354
  {
1530
2355
  field: f,
@@ -1533,11 +2358,11 @@ function EntityForm({
1533
2358
  onChange: (next) => setValues((v) => ({ ...v, [f.name]: next }))
1534
2359
  }
1535
2360
  ),
1536
- f.form.helpText && /* @__PURE__ */ jsx11("p", { className: "text-xs text-slate-500", children: f.form.helpText })
2361
+ f.form.helpText && /* @__PURE__ */ jsx17("p", { className: "mst-help", children: f.form.helpText })
1537
2362
  ] }, f.name)),
1538
- /* @__PURE__ */ jsxs10("div", { className: "flex justify-end gap-2 border-t border-slate-100 pt-4", children: [
1539
- /* @__PURE__ */ jsx11(Button, { type: "button", variant: "secondary", onClick: onCancel, disabled: saving, children: t.cancel }),
1540
- /* @__PURE__ */ jsx11(Button, { type: "submit", variant: "primary", disabled: saving, children: saving ? t.saving : mode === "create" ? t.create : t.save })
2363
+ /* @__PURE__ */ jsxs15("div", { className: "mst-form__foot", children: [
2364
+ /* @__PURE__ */ jsx17(Button, { type: "button", variant: "secondary", onClick: onCancel, disabled: saving, children: t.cancel }),
2365
+ /* @__PURE__ */ jsx17(Button, { type: "submit", variant: "primary", disabled: saving, children: saving ? t.saving : mode === "create" ? t.create : t.save })
1541
2366
  ] })
1542
2367
  ] })
1543
2368
  ] });
@@ -1566,10 +2391,10 @@ function applyLabels(metadata, labels) {
1566
2391
  }
1567
2392
 
1568
2393
  // src/recents.ts
1569
- import { useCallback as useCallback2, useState as useState9 } from "react";
1570
- var STORAGE_KEY = "maestro-admin:recents";
2394
+ import { useCallback as useCallback4, useState as useState13 } from "react";
2395
+ var STORAGE_KEY2 = "maestro-admin:recents";
1571
2396
  var MAX_RECENTS = 8;
1572
- function read(key) {
2397
+ function read2(key) {
1573
2398
  try {
1574
2399
  const raw = globalThis.localStorage?.getItem(key);
1575
2400
  const parsed = raw ? JSON.parse(raw) : [];
@@ -1578,20 +2403,20 @@ function read(key) {
1578
2403
  return [];
1579
2404
  }
1580
2405
  }
1581
- function write(key, ids) {
2406
+ function write2(key, ids) {
1582
2407
  try {
1583
2408
  globalThis.localStorage?.setItem(key, JSON.stringify(ids));
1584
2409
  } catch {
1585
2410
  }
1586
2411
  }
1587
2412
  function useRecents(scope) {
1588
- const key = scope ? `${STORAGE_KEY}:${scope}` : STORAGE_KEY;
1589
- const [recents, setRecents] = useState9(() => read(key));
1590
- const push = useCallback2(
2413
+ const key = scope ? `${STORAGE_KEY2}:${scope}` : STORAGE_KEY2;
2414
+ const [recents, setRecents] = useState13(() => read2(key));
2415
+ const push = useCallback4(
1591
2416
  (entityId) => {
1592
2417
  setRecents((prev) => {
1593
2418
  const next = [entityId, ...prev.filter((id) => id !== entityId)].slice(0, MAX_RECENTS);
1594
- write(key, next);
2419
+ write2(key, next);
1595
2420
  return next;
1596
2421
  });
1597
2422
  },
@@ -1601,30 +2426,81 @@ function useRecents(scope) {
1601
2426
  }
1602
2427
 
1603
2428
  // src/components/MaestroAdmin.tsx
1604
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2429
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1605
2430
  function AdminShell({ title, labels }) {
1606
2431
  const { data, error, loading } = useMetadata();
1607
2432
  const t = useT();
1608
- const [view, setView] = useState10(void 0);
2433
+ const [view, setView] = useState14({ kind: "hub" });
2434
+ const [paletteOpen, setPaletteOpen] = useState14(false);
1609
2435
  const { recents, push: pushRecent } = useRecents();
1610
- const metadata = useMemo3(() => data ? applyLabels(data, labels) : void 0, [data, labels]);
1611
- const entities = metadata?.entities ?? [];
1612
- const activeId = view?.entityId ?? entities[0]?.id;
2436
+ const metadata = useMemo7(() => data ? applyLabels(data, labels) : void 0, [data, labels]);
2437
+ const entities = useMemo7(() => metadata?.entities ?? [], [metadata]);
2438
+ const activeId = view.kind === "hub" ? void 0 : view.entityId;
1613
2439
  const activeEntity = entities.find((e) => e.id === activeId);
1614
- const effectiveView = view ?? (activeId ? { kind: "list", entityId: activeId } : void 0);
1615
- const openEntity = (entityId) => {
1616
- pushRecent(entityId);
1617
- setView({ kind: "list", entityId });
1618
- };
1619
- const navigateToRecord = (entityId, id) => {
1620
- pushRecent(entityId);
1621
- setView({ kind: "detail", entityId, id });
1622
- };
2440
+ const groupOf = useMemo7(() => {
2441
+ const map = /* @__PURE__ */ new Map();
2442
+ for (const group of buildMenu(entities, labels).groups) {
2443
+ for (const entity of group.entities) map.set(entity.id, group.label);
2444
+ }
2445
+ return map;
2446
+ }, [entities, labels]);
2447
+ const openEntity = useCallback5(
2448
+ (entityId) => {
2449
+ pushRecent(entityId);
2450
+ setView({ kind: "list", entityId });
2451
+ },
2452
+ [pushRecent]
2453
+ );
2454
+ const navigateToRecord = useCallback5(
2455
+ (entityId, id) => {
2456
+ pushRecent(entityId);
2457
+ setView({ kind: "detail", entityId, id });
2458
+ },
2459
+ [pushRecent]
2460
+ );
2461
+ useEffect8(() => {
2462
+ const onKey = (e) => {
2463
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
2464
+ e.preventDefault();
2465
+ setPaletteOpen((open) => !open);
2466
+ }
2467
+ };
2468
+ document.addEventListener("keydown", onKey);
2469
+ return () => document.removeEventListener("keydown", onKey);
2470
+ }, []);
2471
+ const crumbs = useMemo7(() => {
2472
+ const home = { label: t.hub, icon: "hub", onClick: view.kind === "hub" ? void 0 : () => setView({ kind: "hub" }) };
2473
+ if (view.kind === "hub" || !activeEntity) return [home];
2474
+ const entityCrumb = {
2475
+ label: activeEntity.label.plural,
2476
+ icon: entityIcon(activeEntity),
2477
+ onClick: view.kind === "list" ? void 0 : () => setView({ kind: "list", entityId: activeEntity.id })
2478
+ };
2479
+ if (view.kind === "list") return [home, entityCrumb];
2480
+ const leaf = view.kind === "detail" ? { label: view.id } : view.kind === "edit" ? { label: t.editTitle(activeEntity.label.singular) } : { label: view.cloneFromId ? t.cloneTitle(activeEntity.label.singular) : t.newTitle(activeEntity.label.singular) };
2481
+ return [home, entityCrumb, leaf];
2482
+ }, [view, activeEntity, t]);
2483
+ const paletteSections = useMemo7(() => {
2484
+ const goTo = entityCommands(entities, groupOf, openEntity);
2485
+ goTo.unshift({ id: "hub", label: t.hub, icon: "hub", run: () => setView({ kind: "hub" }) });
2486
+ const screen = [];
2487
+ if (activeEntity?.capabilities.create) {
2488
+ screen.push({
2489
+ id: "create",
2490
+ label: t.newTitle(activeEntity.label.singular),
2491
+ icon: "plus",
2492
+ run: () => setView({ kind: "create", entityId: activeEntity.id })
2493
+ });
2494
+ }
2495
+ const sections = [{ id: "goto", label: t.goTo, commands: goTo }];
2496
+ if (screen.length > 0) sections.unshift({ id: "screen", label: t.onThisScreen, commands: screen });
2497
+ return sections;
2498
+ }, [entities, groupOf, openEntity, activeEntity, t]);
1623
2499
  if (loading && !data) {
1624
- return /* @__PURE__ */ jsx12("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx12(Spinner, { label: t.loadingMetadata }) });
2500
+ return /* @__PURE__ */ jsx18("div", { className: "mst-boot", children: /* @__PURE__ */ jsx18(Spinner, { label: t.loadingMetadata }) });
1625
2501
  }
1626
- return /* @__PURE__ */ jsx12(AdminEntitiesProvider, { value: entities, children: /* @__PURE__ */ jsxs11("div", { className: "flex h-screen overflow-hidden bg-white text-slate-800", children: [
1627
- /* @__PURE__ */ jsx12(
2502
+ return /* @__PURE__ */ jsxs16(AdminEntitiesProvider, { value: entities, children: [
2503
+ /* @__PURE__ */ jsx18(
1628
2504
  Sidebar,
1629
2505
  {
1630
2506
  entities,
@@ -1632,83 +2508,113 @@ function AdminShell({ title, labels }) {
1632
2508
  title,
1633
2509
  labels,
1634
2510
  recents,
1635
- onSelect: openEntity
2511
+ onSelect: openEntity,
2512
+ onHome: () => setView({ kind: "hub" }),
2513
+ homeActive: view.kind === "hub"
1636
2514
  }
1637
2515
  ),
1638
- /* @__PURE__ */ jsxs11("main", { className: "flex-1 overflow-y-auto p-6", children: [
1639
- error && /* @__PURE__ */ jsx12(ErrorBanner, { error }),
1640
- activeEntity && effectiveView && effectiveView.kind === "list" && /* @__PURE__ */ jsx12(
1641
- EntityList,
1642
- {
1643
- entity: activeEntity,
1644
- onOpen: (id) => setView({ kind: "detail", entityId: activeEntity.id, id }),
1645
- onCreate: () => setView({ kind: "create", entityId: activeEntity.id }),
1646
- onEdit: (id) => setView({ kind: "edit", entityId: activeEntity.id, id }),
1647
- onClone: (id) => setView({ kind: "create", entityId: activeEntity.id, cloneFromId: id }),
1648
- onNavigate: navigateToRecord
1649
- }
1650
- ),
1651
- activeEntity && effectiveView?.kind === "detail" && /* @__PURE__ */ jsx12(
1652
- EntityDetail,
1653
- {
1654
- entity: activeEntity,
1655
- id: effectiveView.id,
1656
- onBack: () => setView({ kind: "list", entityId: activeEntity.id }),
1657
- onEdit: () => setView({ kind: "edit", entityId: activeEntity.id, id: effectiveView.id }),
1658
- onClone: () => setView({ kind: "create", entityId: activeEntity.id, cloneFromId: effectiveView.id }),
1659
- onDeleted: () => setView({ kind: "list", entityId: activeEntity.id }),
1660
- onNavigate: navigateToRecord
1661
- }
1662
- ),
1663
- activeEntity && (effectiveView?.kind === "create" || effectiveView?.kind === "edit") && /* @__PURE__ */ jsx12(
1664
- EntityForm,
1665
- {
1666
- entity: activeEntity,
1667
- mode: effectiveView.kind,
1668
- id: effectiveView.kind === "edit" ? effectiveView.id : void 0,
1669
- cloneFromId: effectiveView.kind === "create" ? effectiveView.cloneFromId : void 0,
1670
- onCancel: () => setView({ kind: "list", entityId: activeEntity.id }),
1671
- onSaved: () => setView({ kind: "list", entityId: activeEntity.id })
1672
- }
1673
- )
1674
- ] })
1675
- ] }) });
2516
+ /* @__PURE__ */ jsxs16("div", { className: "mst-main", children: [
2517
+ /* @__PURE__ */ jsx18(Header, { crumbs, onOpenPalette: () => setPaletteOpen(true) }),
2518
+ /* @__PURE__ */ jsxs16("div", { className: "mst-content", children: [
2519
+ error && /* @__PURE__ */ jsx18(ErrorBanner, { error }),
2520
+ view.kind === "hub" && /* @__PURE__ */ jsx18(Hub, { entities, labels, onOpen: openEntity }),
2521
+ activeEntity && view.kind === "list" && /* @__PURE__ */ jsx18(
2522
+ EntityList,
2523
+ {
2524
+ entity: activeEntity,
2525
+ onOpen: (id) => setView({ kind: "detail", entityId: activeEntity.id, id }),
2526
+ onCreate: () => setView({ kind: "create", entityId: activeEntity.id }),
2527
+ onEdit: (id) => setView({ kind: "edit", entityId: activeEntity.id, id }),
2528
+ onClone: (id) => setView({ kind: "create", entityId: activeEntity.id, cloneFromId: id }),
2529
+ onNavigate: navigateToRecord
2530
+ }
2531
+ ),
2532
+ activeEntity && view.kind === "detail" && /* @__PURE__ */ jsx18(
2533
+ EntityDetail,
2534
+ {
2535
+ entity: activeEntity,
2536
+ id: view.id,
2537
+ onBack: () => setView({ kind: "list", entityId: activeEntity.id }),
2538
+ onEdit: () => setView({ kind: "edit", entityId: activeEntity.id, id: view.id }),
2539
+ onClone: () => setView({ kind: "create", entityId: activeEntity.id, cloneFromId: view.id }),
2540
+ onDeleted: () => setView({ kind: "list", entityId: activeEntity.id }),
2541
+ onNavigate: navigateToRecord
2542
+ }
2543
+ ),
2544
+ activeEntity && (view.kind === "create" || view.kind === "edit") && /* @__PURE__ */ jsx18(
2545
+ EntityForm,
2546
+ {
2547
+ entity: activeEntity,
2548
+ mode: view.kind,
2549
+ id: view.kind === "edit" ? view.id : void 0,
2550
+ cloneFromId: view.kind === "create" ? view.cloneFromId : void 0,
2551
+ onCancel: () => setView({ kind: "list", entityId: activeEntity.id }),
2552
+ onSaved: () => setView({ kind: "list", entityId: activeEntity.id })
2553
+ }
2554
+ )
2555
+ ] })
2556
+ ] }),
2557
+ paletteOpen && /* @__PURE__ */ jsx18(CommandPalette, { sections: paletteSections, onClose: () => setPaletteOpen(false) })
2558
+ ] });
1676
2559
  }
1677
2560
  function MaestroAdmin(props) {
1678
- const client = useMemo3(
2561
+ const client = useMemo7(
1679
2562
  () => props.client ?? new MaestroClient({ apiUrl: props.apiUrl ?? "", headers: props.headers }),
1680
2563
  [props.client, props.apiUrl, props.headers]
1681
2564
  );
1682
- const strings = useMemo3(() => stringsFor(props.locale ?? "en"), [props.locale]);
1683
- return /* @__PURE__ */ jsx12(AdminClientProvider, { value: client, children: /* @__PURE__ */ jsx12(I18nProvider, { value: strings, children: /* @__PURE__ */ jsx12(AdminShell, { title: props.title ?? "Maestro Admin", labels: props.labels }) }) });
2565
+ const strings = useMemo7(() => stringsFor(props.locale ?? "en"), [props.locale]);
2566
+ const [root, setRoot] = useState14(null);
2567
+ return /* @__PURE__ */ jsx18(AdminClientProvider, { value: client, children: /* @__PURE__ */ jsx18(I18nProvider, { value: strings, children: /* @__PURE__ */ jsx18(MaestroThemeProvider, { theme: props.theme, className: "mst-root", rootRef: setRoot, children: /* @__PURE__ */ jsx18(AdminRootProvider, { value: root, children: /* @__PURE__ */ jsx18(AdminShell, { title: props.title ?? "Maestro Admin", labels: props.labels }) }) }) }) });
1684
2568
  }
1685
2569
  export {
1686
2570
  AdminClientProvider,
2571
+ Badge,
2572
+ Button,
2573
+ CommandPalette,
2574
+ EmptyState,
1687
2575
  EntityDetail,
1688
2576
  EntityForm,
1689
2577
  EntityList,
2578
+ ErrorBanner,
1690
2579
  FieldInput,
1691
2580
  FieldValue,
2581
+ Header,
2582
+ Hub,
1692
2583
  I18nProvider,
2584
+ Icon,
2585
+ IconButton,
1693
2586
  JsonInput,
2587
+ MAESTRO_THEMES,
1694
2588
  MaestroAdmin,
1695
2589
  MaestroApiError,
1696
2590
  MaestroClient,
2591
+ MaestroMark,
2592
+ MaestroSeal,
2593
+ MaestroThemeProvider,
1697
2594
  ObjectValue,
1698
2595
  Sidebar,
2596
+ Spinner,
2597
+ Toast,
1699
2598
  applyLabels,
1700
2599
  buildMenu,
1701
2600
  detailFields,
2601
+ entityCommands,
2602
+ entityIcon,
1702
2603
  formFields,
1703
2604
  listFields,
1704
2605
  primaryListFields,
1705
2606
  stringsFor,
2607
+ useAdminRoot,
1706
2608
  useAsync,
1707
2609
  useClient,
2610
+ useColumnPrefs,
2611
+ useEntities,
2612
+ useEntity,
1708
2613
  useEntityList,
1709
2614
  useEntityRecord,
1710
2615
  useMetadata,
1711
2616
  useRecents,
1712
- useT
2617
+ useT,
2618
+ useTheme
1713
2619
  };
1714
2620
  //# sourceMappingURL=index.js.map