@ai-matrx/detail 0.1.67 → 0.1.69

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog — @ai-matrx/detail
2
2
 
3
+ ## 0.1.69
4
+ ### A shell with its own menu gets the actions as data
5
+
6
+ - `DetailShellSlots.menuActions: DetailMenuAction[]` (open as window / docked / page, copy record id — the overflow menu's entries as `{ id, label, onSelect }`) and `DetailShellSlots.navActions` (previous / next only). `DetailActions` takes `optional="none"`; `useDetailMenuActions(core)` is exported.
7
+ - Why: a record open as a canvas tab drew "More record actions" (…) beside the pane's own menu (⋮). A shell whose chrome has a menu offers `menuActions` there and renders `navActions`.
8
+ - Consumer action: a custom shell keeps working (`actions` is unchanged); one with its own menu may switch to `navActions` + `menuActions`.
9
+
10
+ ## 0.1.68
11
+ ### Custom fields on every record view (lane 7 STANDARD-TABLES W5)
12
+
13
+ - `DetailHostPorts.customFields?: { Section: ComponentType<{ token; id }> }` — the host binds the
14
+ organization's custom-fields section once; `DetailBody` renders it after Fields for every record
15
+ with an `entityToken` (window, docked, page, and a peek that renders the body).
16
+ - Consumer action: bind `customFields.Section` in the host (matrx-frontend `DetailHost.tsx`). Optional;
17
+ an unbound host shows no section.
18
+
3
19
  ## 0.1.67
4
20
 
5
21
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -272,6 +272,12 @@ interface DetailHistoryEntry {
272
272
  }
273
273
 
274
274
  /** Props every shell receives; the package fills the slots, the host owns the chrome. */
275
+ /** One of the record's optional controls as data (a shell's menu draws it). */
276
+ interface DetailMenuAction {
277
+ readonly id: string;
278
+ readonly label: string;
279
+ readonly onSelect: () => void;
280
+ }
275
281
  interface DetailShellSlots {
276
282
  /** Accessible plain-text title (window chrome, drawer title, document title). */
277
283
  title: string;
@@ -279,6 +285,13 @@ interface DetailShellSlots {
279
285
  titleNode: ReactNode;
280
286
  /** Presentation switcher · previous / next · copy id. Compact. */
281
287
  actions: ReactNode;
288
+ /**
289
+ * Previous / next only — for a shell whose chrome has its OWN menu: it puts
290
+ * `menuActions` there instead of a second overflow button ("…" beside "⋮").
291
+ */
292
+ navActions: ReactNode;
293
+ /** The optional controls (open as window / docked / page, copy id) as data, for a shell's own menu. */
294
+ menuActions: readonly DetailMenuAction[];
282
295
  children: ReactNode;
283
296
  onClose: () => void;
284
297
  }
@@ -475,6 +488,23 @@ interface DetailHostPorts {
475
488
  row: DetailRow | null;
476
489
  }>;
477
490
  };
491
+ /**
492
+ * 🚨 CUSTOM FIELDS ON EVERY RECORD VIEW (lane 7 STANDARD-TABLES W5). The ONE
493
+ * place a window, a docked panel, the `/detail` page and a record peek get the
494
+ * organization's own fields for a standard record: the host binds its section
495
+ * once, and the body renders it after Fields whenever the record has an
496
+ * `entityToken`. The section is given only the token and the id — it reads the
497
+ * row's organization itself (never the active organization) and says in one
498
+ * line why it shows nothing, so it is never silently absent. A peek binds a
499
+ * compact section through a nested provider (providers merge). Optional: a
500
+ * host without custom fields binds nothing and the section is absent.
501
+ */
502
+ customFields?: {
503
+ Section: ComponentType<{
504
+ token: string;
505
+ id: string;
506
+ }>;
507
+ };
478
508
  notify: {
479
509
  error: (message: string) => void;
480
510
  success: (message: string) => void;
@@ -521,4 +551,4 @@ declare function requireResolveType(host: DetailHostPorts): NonNullable<DetailHo
521
551
  /** A presentation's shell, or an error naming which entry forgot to bind it. */
522
552
  declare function requireShell<K extends keyof DetailShells>(shells: DetailShells, which: K): NonNullable<DetailShells[K]>;
523
553
 
524
- export { type DetailRef as D, type DetailInstanceData as a, type DetailPresentation as b, type DetailRecordType as c, type DetailStatus as d, type DetailRow as e, type DetailField as f, type DetailSourceHealth as g, type DetailFrameContext as h, type DetailListContext as i, type DetailLoadState as j, type DetailOpenRequest as k, type DetailDockedShellProps as l, type DetailEffectivePresentation as m, type DetailHostPorts as n, DetailHostProvider as o, type DetailPageShellProps as p, type DetailPresentationSetting as q, type DetailShellSlots as r, type DetailShells as s, type DetailWindowShellProps as t, requireResolveType as u, requireShell as v, useDetailHost as w };
554
+ export { type DetailRef as D, type DetailInstanceData as a, type DetailPresentation as b, type DetailRecordType as c, type DetailStatus as d, type DetailRow as e, type DetailField as f, type DetailSourceHealth as g, type DetailFrameContext as h, type DetailListContext as i, type DetailLoadState as j, type DetailOpenRequest as k, type DetailMenuAction as l, type DetailDockedShellProps as m, type DetailEffectivePresentation as n, type DetailHostPorts as o, DetailHostProvider as p, type DetailPageShellProps as q, type DetailPresentationSetting as r, type DetailShellSlots as s, type DetailShells as t, type DetailWindowShellProps as u, requireResolveType as v, requireShell as w, useDetailHost as x };
@@ -272,6 +272,12 @@ interface DetailHistoryEntry {
272
272
  }
273
273
 
274
274
  /** Props every shell receives; the package fills the slots, the host owns the chrome. */
275
+ /** One of the record's optional controls as data (a shell's menu draws it). */
276
+ interface DetailMenuAction {
277
+ readonly id: string;
278
+ readonly label: string;
279
+ readonly onSelect: () => void;
280
+ }
275
281
  interface DetailShellSlots {
276
282
  /** Accessible plain-text title (window chrome, drawer title, document title). */
277
283
  title: string;
@@ -279,6 +285,13 @@ interface DetailShellSlots {
279
285
  titleNode: ReactNode;
280
286
  /** Presentation switcher · previous / next · copy id. Compact. */
281
287
  actions: ReactNode;
288
+ /**
289
+ * Previous / next only — for a shell whose chrome has its OWN menu: it puts
290
+ * `menuActions` there instead of a second overflow button ("…" beside "⋮").
291
+ */
292
+ navActions: ReactNode;
293
+ /** The optional controls (open as window / docked / page, copy id) as data, for a shell's own menu. */
294
+ menuActions: readonly DetailMenuAction[];
282
295
  children: ReactNode;
283
296
  onClose: () => void;
284
297
  }
@@ -475,6 +488,23 @@ interface DetailHostPorts {
475
488
  row: DetailRow | null;
476
489
  }>;
477
490
  };
491
+ /**
492
+ * 🚨 CUSTOM FIELDS ON EVERY RECORD VIEW (lane 7 STANDARD-TABLES W5). The ONE
493
+ * place a window, a docked panel, the `/detail` page and a record peek get the
494
+ * organization's own fields for a standard record: the host binds its section
495
+ * once, and the body renders it after Fields whenever the record has an
496
+ * `entityToken`. The section is given only the token and the id — it reads the
497
+ * row's organization itself (never the active organization) and says in one
498
+ * line why it shows nothing, so it is never silently absent. A peek binds a
499
+ * compact section through a nested provider (providers merge). Optional: a
500
+ * host without custom fields binds nothing and the section is absent.
501
+ */
502
+ customFields?: {
503
+ Section: ComponentType<{
504
+ token: string;
505
+ id: string;
506
+ }>;
507
+ };
478
508
  notify: {
479
509
  error: (message: string) => void;
480
510
  success: (message: string) => void;
@@ -521,4 +551,4 @@ declare function requireResolveType(host: DetailHostPorts): NonNullable<DetailHo
521
551
  /** A presentation's shell, or an error naming which entry forgot to bind it. */
522
552
  declare function requireShell<K extends keyof DetailShells>(shells: DetailShells, which: K): NonNullable<DetailShells[K]>;
523
553
 
524
- export { type DetailRef as D, type DetailInstanceData as a, type DetailPresentation as b, type DetailRecordType as c, type DetailStatus as d, type DetailRow as e, type DetailField as f, type DetailSourceHealth as g, type DetailFrameContext as h, type DetailListContext as i, type DetailLoadState as j, type DetailOpenRequest as k, type DetailDockedShellProps as l, type DetailEffectivePresentation as m, type DetailHostPorts as n, DetailHostProvider as o, type DetailPageShellProps as p, type DetailPresentationSetting as q, type DetailShellSlots as r, type DetailShells as s, type DetailWindowShellProps as t, requireResolveType as u, requireShell as v, useDetailHost as w };
554
+ export { type DetailRef as D, type DetailInstanceData as a, type DetailPresentation as b, type DetailRecordType as c, type DetailStatus as d, type DetailRow as e, type DetailField as f, type DetailSourceHealth as g, type DetailFrameContext as h, type DetailListContext as i, type DetailLoadState as j, type DetailOpenRequest as k, type DetailMenuAction as l, type DetailDockedShellProps as m, type DetailEffectivePresentation as n, type DetailHostPorts as o, DetailHostProvider as p, type DetailPageShellProps as q, type DetailPresentationSetting as r, type DetailShellSlots as s, type DetailShells as t, type DetailWindowShellProps as u, requireResolveType as v, requireShell as w, useDetailHost as x };
package/dist/react.cjs CHANGED
@@ -40,6 +40,7 @@ __export(react_exports, {
40
40
  useDetailHealth: () => useDetailHealth,
41
41
  useDetailHost: () => useDetailHost,
42
42
  useDetailKeyboard: () => useDetailKeyboard,
43
+ useDetailMenuActions: () => useDetailMenuActions,
43
44
  useDetailRecord: () => useDetailRecord,
44
45
  useOpenDetail: () => useOpenDetail
45
46
  });
@@ -759,7 +760,27 @@ function DetailRecordMeta({ core }) {
759
760
  }
760
761
  );
761
762
  }
762
- function DetailActions({ core }) {
763
+ function useDetailMenuActions(core) {
764
+ const host = useDetailHost();
765
+ return [
766
+ ...PRESENTATIONS.filter((p) => p.value !== core.presentation).map(({ value, label }) => ({
767
+ id: `detail-open-${value}`,
768
+ label,
769
+ onSelect: () => core.switchTo(value)
770
+ })),
771
+ {
772
+ id: "detail-copy-id",
773
+ label: "Copy record id",
774
+ onSelect: () => {
775
+ void host.copyText(core.ref.id).then((ok) => {
776
+ if (ok) host.notify.success("Record id copied");
777
+ else host.notify.error("Could not copy the record id to the clipboard.");
778
+ });
779
+ }
780
+ }
781
+ ];
782
+ }
783
+ function DetailActions({ core, optional = "inline" }) {
763
784
  const host = useDetailHost();
764
785
  const [copied, setCopied] = (0, import_react7.useState)(false);
765
786
  const [menuOpen, setMenuOpen] = (0, import_react7.useState)(false);
@@ -824,98 +845,100 @@ function DetailActions({ core }) {
824
845
  ),
825
846
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "mx-1 h-4 w-px bg-border", "aria-hidden": true })
826
847
  ] }) : null,
827
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
828
- "span",
829
- {
830
- className: "flex items-center gap-0.5 @max-[26rem]/window-header:hidden",
831
- "data-detail-actions-collapsible": true,
832
- children: [
833
- others.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
834
- "button",
835
- {
836
- type: "button",
837
- className: ICON_BUTTON,
838
- onClick: () => core.switchTo(value),
839
- "aria-label": label,
840
- title: label,
841
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "h-4 w-4" })
842
- },
843
- value
844
- )),
845
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
846
- "button",
847
- {
848
- type: "button",
849
- onClick: () => void copyId(),
850
- className: ICON_BUTTON,
851
- "aria-label": "Copy record id",
852
- title: "Copy record id",
853
- "data-detail-copy-id": true,
854
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, { className: "h-3.5 w-3.5 text-primary" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CopyIcon, { className: "h-3.5 w-3.5" })
855
- }
856
- )
857
- ]
858
- }
859
- ),
860
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "relative hidden @max-[26rem]/window-header:inline-flex", ref: menuRef, children: [
861
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
862
- "button",
848
+ optional === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
849
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
850
+ "span",
863
851
  {
864
- type: "button",
865
- className: (0, import_design_system.cn)(ICON_BUTTON, "hidden @max-[26rem]/window-header:inline-flex"),
866
- onClick: () => setMenuOpen((wasOpen) => !wasOpen),
867
- "aria-label": "More record actions",
868
- title: "More record actions",
869
- "aria-haspopup": "menu",
870
- "aria-expanded": menuOpen,
871
- "data-detail-actions-overflow": true,
872
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MoreHorizontalIcon, { className: "h-4 w-4" })
873
- }
874
- ),
875
- menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
876
- "div",
877
- {
878
- role: "menu",
879
- "aria-label": "Record actions",
880
- className: "absolute right-0 top-full z-50 mt-1 min-w-44 rounded-md border border-border bg-card p-1 shadow-md",
881
- "data-detail-actions-menu": true,
852
+ className: "flex items-center gap-0.5 @max-[26rem]/window-header:hidden",
853
+ "data-detail-actions-collapsible": true,
882
854
  children: [
883
- others.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
855
+ others.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
884
856
  "button",
885
857
  {
886
858
  type: "button",
887
- role: "menuitem",
888
- className: MENU_ITEM,
889
- onClick: () => {
890
- setMenuOpen(false);
891
- core.switchTo(value);
892
- },
893
- children: [
894
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
895
- label
896
- ]
859
+ className: ICON_BUTTON,
860
+ onClick: () => core.switchTo(value),
861
+ "aria-label": label,
862
+ title: label,
863
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "h-4 w-4" })
897
864
  },
898
865
  value
899
866
  )),
900
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
867
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
901
868
  "button",
902
869
  {
903
870
  type: "button",
904
- role: "menuitem",
905
- className: MENU_ITEM,
906
- onClick: () => {
907
- setMenuOpen(false);
908
- void copyId();
909
- },
910
- children: [
911
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CopyIcon, { className: "h-3.5 w-3.5 shrink-0" }),
912
- "Copy record id"
913
- ]
871
+ onClick: () => void copyId(),
872
+ className: ICON_BUTTON,
873
+ "aria-label": "Copy record id",
874
+ title: "Copy record id",
875
+ "data-detail-copy-id": true,
876
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, { className: "h-3.5 w-3.5 text-primary" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CopyIcon, { className: "h-3.5 w-3.5" })
914
877
  }
915
878
  )
916
879
  ]
917
880
  }
918
- ) : null
881
+ ),
882
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "relative hidden @max-[26rem]/window-header:inline-flex", ref: menuRef, children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
884
+ "button",
885
+ {
886
+ type: "button",
887
+ className: (0, import_design_system.cn)(ICON_BUTTON, "hidden @max-[26rem]/window-header:inline-flex"),
888
+ onClick: () => setMenuOpen((wasOpen) => !wasOpen),
889
+ "aria-label": "More record actions",
890
+ title: "More record actions",
891
+ "aria-haspopup": "menu",
892
+ "aria-expanded": menuOpen,
893
+ "data-detail-actions-overflow": true,
894
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MoreHorizontalIcon, { className: "h-4 w-4" })
895
+ }
896
+ ),
897
+ menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
898
+ "div",
899
+ {
900
+ role: "menu",
901
+ "aria-label": "Record actions",
902
+ className: "absolute right-0 top-full z-50 mt-1 min-w-44 rounded-md border border-border bg-card p-1 shadow-md",
903
+ "data-detail-actions-menu": true,
904
+ children: [
905
+ others.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
906
+ "button",
907
+ {
908
+ type: "button",
909
+ role: "menuitem",
910
+ className: MENU_ITEM,
911
+ onClick: () => {
912
+ setMenuOpen(false);
913
+ core.switchTo(value);
914
+ },
915
+ children: [
916
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
917
+ label
918
+ ]
919
+ },
920
+ value
921
+ )),
922
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
923
+ "button",
924
+ {
925
+ type: "button",
926
+ role: "menuitem",
927
+ className: MENU_ITEM,
928
+ onClick: () => {
929
+ setMenuOpen(false);
930
+ void copyId();
931
+ },
932
+ children: [
933
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CopyIcon, { className: "h-3.5 w-3.5 shrink-0" }),
934
+ "Copy record id"
935
+ ]
936
+ }
937
+ )
938
+ ]
939
+ }
940
+ ) : null
941
+ ] })
919
942
  ] })
920
943
  ] });
921
944
  }
@@ -1453,6 +1476,13 @@ function HistorySection({ core }) {
1453
1476
  )) })
1454
1477
  ] });
1455
1478
  }
1479
+ function CustomFieldsPort({ core }) {
1480
+ const host = useDetailHost();
1481
+ const Section = host.customFields?.Section;
1482
+ const token = core.entityToken;
1483
+ if (!Section || !token) return null;
1484
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Section, { token, id: core.ref.id });
1485
+ }
1456
1486
  function DetailBody({ core }) {
1457
1487
  const extra = core.recordType?.extraSections && core.frameCtx ? core.recordType.extraSections(core.row, core.frameCtx) : [];
1458
1488
  const Frame = core.recordType?.Frame ?? null;
@@ -1466,6 +1496,7 @@ function DetailBody({ core }) {
1466
1496
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-6 p-4", children: [
1467
1497
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FieldsSection, { core }),
1468
1498
  core.status === "ready" || core.status === "none" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1499
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CustomFieldsPort, { core }),
1469
1500
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AssociationsSection, { core }),
1470
1501
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(HistorySection, { core }),
1471
1502
  extra.map((section) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "space-y-2", "data-detail-section": section.id, children: [
@@ -1545,6 +1576,7 @@ function KeyboardRoot({ core, children }) {
1545
1576
  function DetailWindowPresentation({ data, onClose }) {
1546
1577
  const host = useDetailHost();
1547
1578
  const core = useDetailCore(data, "window", { onClose });
1579
+ const menuActions = useDetailMenuActions(core);
1548
1580
  const Shell = requireShell(host.shells, "Window");
1549
1581
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1550
1582
  Shell,
@@ -1555,6 +1587,8 @@ function DetailWindowPresentation({ data, onClose }) {
1555
1587
  title: core.title,
1556
1588
  titleNode: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailTitle, { core }) }),
1557
1589
  actions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardActionsSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailActions, { core }) }),
1590
+ navActions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardActionsSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailActions, { core, optional: "none" }) }),
1591
+ menuActions,
1558
1592
  onClose: core.close,
1559
1593
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardRoot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailBody, { core }) })
1560
1594
  }
@@ -1563,6 +1597,7 @@ function DetailWindowPresentation({ data, onClose }) {
1563
1597
  function DetailDockedPresentation({ data, onClose }) {
1564
1598
  const host = useDetailHost();
1565
1599
  const core = useDetailCore(data, "docked", { onClose });
1600
+ const menuActions = useDetailMenuActions(core);
1566
1601
  const Shell = requireShell(host.shells, "Docked");
1567
1602
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1568
1603
  Shell,
@@ -1572,6 +1607,8 @@ function DetailDockedPresentation({ data, onClose }) {
1572
1607
  title: core.title,
1573
1608
  titleNode: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailTitle, { core }) }),
1574
1609
  actions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardActionsSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailActions, { core }) }),
1610
+ navActions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardActionsSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailActions, { core, optional: "none" }) }),
1611
+ menuActions,
1575
1612
  onClose: core.close,
1576
1613
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardRoot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailBody, { core }) })
1577
1614
  }
@@ -1580,6 +1617,7 @@ function DetailDockedPresentation({ data, onClose }) {
1580
1617
  function DetailPagePresentation({ data }) {
1581
1618
  const host = useDetailHost();
1582
1619
  const core = useDetailCore(data, "page", {});
1620
+ const menuActions = useDetailMenuActions(core);
1583
1621
  const Shell = requireShell(host.shells, "Page");
1584
1622
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1585
1623
  Shell,
@@ -1588,6 +1626,8 @@ function DetailPagePresentation({ data }) {
1588
1626
  title: core.title,
1589
1627
  titleNode: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailTitle, { core }) }),
1590
1628
  actions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardActionsSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailActions, { core }) }),
1629
+ navActions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardActionsSlot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailActions, { core, optional: "none" }) }),
1630
+ menuActions,
1591
1631
  onClose: core.close,
1592
1632
  onBack: core.leave,
1593
1633
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KeyboardRoot, { core, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DetailBody, { core }) })