@webiny/app-admin-ui 6.6.0-alpha.0 → 6.6.0-alpha.2

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.
Files changed (68) hide show
  1. package/Breadcrumbs/Breadcrumbs.d.ts +14 -0
  2. package/Breadcrumbs/Breadcrumbs.js +74 -0
  3. package/Breadcrumbs/Breadcrumbs.js.map +1 -0
  4. package/CommandPalette/CommandPalette.d.ts +6 -0
  5. package/CommandPalette/CommandPalette.js +202 -0
  6. package/CommandPalette/CommandPalette.js.map +1 -0
  7. package/CommandPalette/components/AiModeBadge.d.ts +3 -0
  8. package/CommandPalette/components/AiModeBadge.js +11 -0
  9. package/CommandPalette/components/AiModeBadge.js.map +1 -0
  10. package/CommandPalette/components/AiSuggestions.d.ts +9 -0
  11. package/CommandPalette/components/AiSuggestions.js +32 -0
  12. package/CommandPalette/components/AiSuggestions.js.map +1 -0
  13. package/CommandPalette/components/AiTurn.d.ts +11 -0
  14. package/CommandPalette/components/AiTurn.js +61 -0
  15. package/CommandPalette/components/AiTurn.js.map +1 -0
  16. package/CommandPalette/components/AnswerSkeleton.d.ts +6 -0
  17. package/CommandPalette/components/AnswerSkeleton.js +20 -0
  18. package/CommandPalette/components/AnswerSkeleton.js.map +1 -0
  19. package/CommandPalette/components/ApprovalPlan.d.ts +16 -0
  20. package/CommandPalette/components/ApprovalPlan.js +56 -0
  21. package/CommandPalette/components/ApprovalPlan.js.map +1 -0
  22. package/CommandPalette/components/CommandDetail.d.ts +13 -0
  23. package/CommandPalette/components/CommandDetail.js +39 -0
  24. package/CommandPalette/components/CommandDetail.js.map +1 -0
  25. package/CommandPalette/components/CommandItemRow.d.ts +5 -0
  26. package/CommandPalette/components/CommandItemRow.js +41 -0
  27. package/CommandPalette/components/CommandItemRow.js.map +1 -0
  28. package/CommandPalette/components/GroupHeading.d.ts +4 -0
  29. package/CommandPalette/components/GroupHeading.js +10 -0
  30. package/CommandPalette/components/GroupHeading.js.map +1 -0
  31. package/CommandPalette/components/HintIcon.d.ts +4 -0
  32. package/CommandPalette/components/HintIcon.js +11 -0
  33. package/CommandPalette/components/HintIcon.js.map +1 -0
  34. package/CommandPalette/components/Kbd.d.ts +4 -0
  35. package/CommandPalette/components/Kbd.js +14 -0
  36. package/CommandPalette/components/Kbd.js.map +1 -0
  37. package/CommandPalette/components/NoResults.d.ts +10 -0
  38. package/CommandPalette/components/NoResults.js +33 -0
  39. package/CommandPalette/components/NoResults.js.map +1 -0
  40. package/CommandPalette/components/PaletteFooter.d.ts +13 -0
  41. package/CommandPalette/components/PaletteFooter.js +18 -0
  42. package/CommandPalette/components/PaletteFooter.js.map +1 -0
  43. package/CommandPalette/components/ToolChip.d.ts +7 -0
  44. package/CommandPalette/components/ToolChip.js +32 -0
  45. package/CommandPalette/components/ToolChip.js.map +1 -0
  46. package/CommandPalette/components/index.d.ts +13 -0
  47. package/CommandPalette/components/index.js +13 -0
  48. package/CommandPalette/constants.d.ts +8 -0
  49. package/CommandPalette/constants.js +5 -0
  50. package/CommandPalette/constants.js.map +1 -0
  51. package/CommandPalette/deriveRows.d.ts +19 -0
  52. package/CommandPalette/deriveRows.js +100 -0
  53. package/CommandPalette/deriveRows.js.map +1 -0
  54. package/CommandPalette/modes/PaletteMode.d.ts +73 -0
  55. package/CommandPalette/modes/PaletteMode.js +0 -0
  56. package/CommandPalette/modes/createAiMode.d.ts +13 -0
  57. package/CommandPalette/modes/createAiMode.js +80 -0
  58. package/CommandPalette/modes/createAiMode.js.map +1 -0
  59. package/CommandPalette/modes/index.d.ts +2 -0
  60. package/CommandPalette/modes/index.js +1 -0
  61. package/CommandPalette/types.d.ts +15 -0
  62. package/CommandPalette/types.js +0 -0
  63. package/CommandPalette/usePaletteHotkeys.d.ts +14 -0
  64. package/CommandPalette/usePaletteHotkeys.js +35 -0
  65. package/CommandPalette/usePaletteHotkeys.js.map +1 -0
  66. package/Layout.js +6 -2
  67. package/Layout.js.map +1 -1
  68. package/package.json +7 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/ApprovalPlan.js","sources":["../../../src/CommandPalette/components/ApprovalPlan.tsx"],"sourcesContent":["import React from \"react\";\nimport { Button } from \"@webiny/admin-ui\";\nimport { cn } from \"@webiny/admin-ui\";\nimport { Icon } from \"@webiny/admin-ui\";\nimport { Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as BoltIcon } from \"@webiny/icons/bolt.svg\";\nimport { ReactComponent as WarningIcon } from \"@webiny/icons/warning.svg\";\nimport type { AiChatPendingApproval } from \"@webiny/app-admin\";\n\nexport interface ApprovalPlanProps {\n approvals: AiChatPendingApproval[];\n busy: boolean;\n onApprove: () => void;\n onReject: () => void;\n}\n\n/**\n * The confirm gate for a proposed change.\n *\n * Arguments are shown as raw JSON on purpose. A prose summary would be the assistant describing its\n * own request — and the assistant is exactly what is not yet trusted here. What the user approves has\n * to be what actually runs.\n */\nexport const ApprovalPlan = ({ approvals, busy, onApprove, onReject }: ApprovalPlanProps) => {\n if (approvals.length === 0) {\n return null;\n }\n\n const destructive = approvals.some(approval => approval.destructive);\n\n let heading = `Approve ${approvals.length} changes`;\n if (approvals.length === 1) {\n heading = `Approve: ${approvals[0].title ?? approvals[0].toolName}`;\n }\n\n return (\n <div\n className={cn(\n \"mt-sm overflow-hidden rounded-md border border-neutral-dimmed\",\n destructive ? \"bg-destructive-subtle\" : \"bg-warning-subtle\"\n )}\n >\n <div className=\"flex items-center gap-sm border-b border-neutral-dimmed px-sm py-xs-plus\">\n <Icon\n icon={destructive ? <WarningIcon /> : <BoltIcon />}\n size=\"sm\"\n label=\"\"\n color=\"inherit\"\n className={destructive ? \"text-destructive-primary\" : undefined}\n />\n <Text size=\"sm\" className=\"font-semibold text-neutral-primary\">\n {heading}\n </Text>\n {destructive ? (\n <Text size=\"sm\" className=\"ml-auto text-destructive-primary\">\n Cannot be undone\n </Text>\n ) : null}\n </div>\n\n <div className=\"bg-neutral-base px-sm py-xs-plus\">\n {approvals.map(approval => (\n <div key={approval.approvalId} className=\"mb-xs\">\n <Text as=\"div\" size=\"sm\" className=\"font-mono text-neutral-strong\">\n {approval.toolName}\n </Text>\n <pre className=\"mt-xxs overflow-x-auto rounded bg-neutral-subtle p-sm text-xs text-neutral-strong\">\n {JSON.stringify(approval.input, null, 2)}\n </pre>\n </div>\n ))}\n\n <div className=\"mt-sm flex items-center gap-sm\">\n <Button variant=\"primary\" text=\"Run\" disabled={busy} onClick={onApprove} />\n <Button variant=\"secondary\" text=\"Reject\" disabled={busy} onClick={onReject} />\n <Text size=\"sm\" className=\"text-neutral-muted\">\n Nothing runs until you confirm.\n </Text>\n </div>\n </div>\n </div>\n );\n};\n"],"names":["ApprovalPlan","approvals","busy","onApprove","onReject","destructive","approval","heading","cn","Icon","WarningIcon","BoltIcon","undefined","Text","JSON","Button"],"mappings":";;;;AAuBO,MAAMA,eAAe,CAAC,EAAEC,SAAS,EAAEC,IAAI,EAAEC,SAAS,EAAEC,QAAQ,EAAqB;IACpF,IAAIH,AAAqB,MAArBA,UAAU,MAAM,EAChB,OAAO;IAGX,MAAMI,cAAcJ,UAAU,IAAI,CAACK,CAAAA,WAAYA,SAAS,WAAW;IAEnE,IAAIC,UAAU,CAAC,QAAQ,EAAEN,UAAU,MAAM,CAAC,QAAQ,CAAC;IACnD,IAAIA,AAAqB,MAArBA,UAAU,MAAM,EAChBM,UAAU,CAAC,SAAS,EAAEN,SAAS,CAAC,EAAE,CAAC,KAAK,IAAIA,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE;IAGvE,OAAO,WAAP,GACI,oBAAC;QACG,WAAWO,GACP,iEACAH,cAAc,0BAA0B;qBAG5C,oBAAC;QAAI,WAAU;qBACX,oBAACI,MAAIA;QACD,MAAMJ,cAAc,WAAdA,GAAc,oBAACK,4BAAWA,QAAAA,WAAAA,GAAM,oBAACC,gBAAQA;QAC/C,MAAK;QACL,OAAM;QACN,OAAM;QACN,WAAWN,cAAc,6BAA6BO;sBAE1D,oBAACC,MAAIA;QAAC,MAAK;QAAK,WAAU;OACrBN,UAEJF,cAAc,WAAdA,GACG,oBAACQ,MAAIA;QAAC,MAAK;QAAK,WAAU;OAAmC,sBAG7D,qBAGR,oBAAC;QAAI,WAAU;OACVZ,UAAU,GAAG,CAACK,CAAAA,WAAAA,WAAAA,GACX,oBAAC;YAAI,KAAKA,SAAS,UAAU;YAAE,WAAU;yBACrC,oBAACO,MAAIA;YAAC,IAAG;YAAM,MAAK;YAAK,WAAU;WAC9BP,SAAS,QAAQ,iBAEtB,oBAAC;YAAI,WAAU;WACVQ,KAAK,SAAS,CAACR,SAAS,KAAK,EAAE,MAAM,qBAKlD,oBAAC;QAAI,WAAU;qBACX,oBAACS,QAAMA;QAAC,SAAQ;QAAU,MAAK;QAAM,UAAUb;QAAM,SAASC;sBAC9D,oBAACY,QAAMA;QAAC,SAAQ;QAAY,MAAK;QAAS,UAAUb;QAAM,SAASE;sBACnE,oBAACS,MAAIA;QAAC,MAAK;QAAK,WAAU;OAAqB;AAOnE"}
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import type { ActiveCommandVm } from "@webiny/app-admin";
3
+ interface CommandDetailProps {
4
+ active: ActiveCommandVm;
5
+ onBack: () => void;
6
+ onClose: () => void;
7
+ }
8
+ /**
9
+ * Renders a command's detail view (e.g. a form) inside the palette. Back returns to the
10
+ * command list; the detail view itself calls onClose when it completes.
11
+ */
12
+ export declare const CommandDetail: ({ active, onBack, onClose }: CommandDetailProps) => React.JSX.Element;
13
+ export {};
@@ -0,0 +1,39 @@
1
+ import react from "react";
2
+ import { IconButton, Text } from "@webiny/admin-ui";
3
+ import { ReactComponent } from "@webiny/icons/arrow_back.svg";
4
+ import { Kbd } from "./Kbd.js";
5
+ const CommandDetail = ({ active, onBack, onClose })=>{
6
+ const { command, DetailView } = active;
7
+ return /*#__PURE__*/ react.createElement("div", {
8
+ style: {
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ flex: 1,
12
+ minHeight: 0
13
+ }
14
+ }, /*#__PURE__*/ react.createElement("div", {
15
+ className: "flex items-center gap-sm border-b border-neutral-subtle px-md py-sm-plus"
16
+ }, /*#__PURE__*/ react.createElement(IconButton, {
17
+ variant: "ghost",
18
+ size: "sm",
19
+ icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
20
+ onClick: onBack,
21
+ "aria-label": "Back"
22
+ }), command.icon, /*#__PURE__*/ react.createElement(Text, {
23
+ size: "md",
24
+ className: "flex-1 font-medium text-neutral-primary"
25
+ }, command.label), /*#__PURE__*/ react.createElement(Kbd, null, "esc")), /*#__PURE__*/ react.createElement("div", {
26
+ style: {
27
+ flex: 1,
28
+ minHeight: 0,
29
+ overflowY: "auto"
30
+ }
31
+ }, /*#__PURE__*/ react.createElement(DetailView, {
32
+ command: command,
33
+ onClose: onClose,
34
+ onBack: onBack
35
+ })));
36
+ };
37
+ export { CommandDetail };
38
+
39
+ //# sourceMappingURL=CommandDetail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/CommandDetail.js","sources":["../../../src/CommandPalette/components/CommandDetail.tsx"],"sourcesContent":["import React from \"react\";\nimport { IconButton, Text } from \"@webiny/admin-ui\";\nimport type { ActiveCommandVm } from \"@webiny/app-admin\";\nimport { ReactComponent as BackIcon } from \"@webiny/icons/arrow_back.svg\";\nimport { Kbd } from \"./Kbd.js\";\n\ninterface CommandDetailProps {\n active: ActiveCommandVm;\n onBack: () => void;\n onClose: () => void;\n}\n\n/**\n * Renders a command's detail view (e.g. a form) inside the palette. Back returns to the\n * command list; the detail view itself calls onClose when it completes.\n */\nexport const CommandDetail = ({ active, onBack, onClose }: CommandDetailProps) => {\n const { command, DetailView } = active;\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", flex: 1, minHeight: 0 }}>\n <div className=\"flex items-center gap-sm border-b border-neutral-subtle px-md py-sm-plus\">\n <IconButton\n variant={\"ghost\"}\n size={\"sm\"}\n icon={<BackIcon />}\n onClick={onBack}\n aria-label=\"Back\"\n />\n {command.icon}\n <Text size=\"md\" className=\"flex-1 font-medium text-neutral-primary\">\n {command.label}\n </Text>\n <Kbd>esc</Kbd>\n </div>\n <div style={{ flex: 1, minHeight: 0, overflowY: \"auto\" }}>\n <DetailView command={command} onClose={onClose} onBack={onBack} />\n </div>\n </div>\n );\n};\n"],"names":["CommandDetail","active","onBack","onClose","command","DetailView","IconButton","BackIcon","Text","Kbd"],"mappings":";;;;AAgBO,MAAMA,gBAAgB,CAAC,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAsB;IACzE,MAAM,EAAEC,OAAO,EAAEC,UAAU,EAAE,GAAGJ;IAEhC,OAAO,WAAP,GACI,oBAAC;QAAI,OAAO;YAAE,SAAS;YAAQ,eAAe;YAAU,MAAM;YAAG,WAAW;QAAE;qBAC1E,oBAAC;QAAI,WAAU;qBACX,oBAACK,YAAUA;QACP,SAAS;QACT,MAAM;QACN,oBAAM,oBAACC,gBAAQA;QACf,SAASL;QACT,cAAW;QAEdE,QAAQ,IAAI,gBACb,oBAACI,MAAIA;QAAC,MAAK;QAAK,WAAU;OACrBJ,QAAQ,KAAK,iBAElB,oBAACK,KAAGA,MAAC,uBAET,oBAAC;QAAI,OAAO;YAAE,MAAM;YAAG,WAAW;YAAG,WAAW;QAAO;qBACnD,oBAACJ,YAAAA;QAAW,SAASD;QAAS,SAASD;QAAS,QAAQD;;AAIxE"}
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ import type { CommandRow } from "../types.js";
3
+ export declare const CommandItemRow: ({ row }: {
4
+ row: CommandRow;
5
+ }) => React.JSX.Element;
@@ -0,0 +1,41 @@
1
+ import react from "react";
2
+ import { Command, useCommandState } from "cmdk";
3
+ import { Icon, Text, cn } from "@webiny/admin-ui";
4
+ import { ReactComponent } from "@webiny/icons/keyboard_return.svg";
5
+ import { Kbd } from "./Kbd.js";
6
+ const CommandItemRow = ({ row })=>{
7
+ const selected = useCommandState((state)=>(state.value || "") === row.value);
8
+ let tile = "border-neutral-dimmed bg-neutral-subtle";
9
+ if (selected) tile = "border-primary bg-primary-subtle";
10
+ return /*#__PURE__*/ react.createElement(Command.Item, {
11
+ value: row.value,
12
+ onSelect: row.onRun,
13
+ className: "flex cursor-pointer items-center gap-sm rounded-md px-sm py-xs-plus data-[selected=true]:bg-neutral-dimmed"
14
+ }, /*#__PURE__*/ react.createElement("div", {
15
+ className: cn("grid size-xl shrink-0 place-items-center rounded-md border", tile)
16
+ }, row.icon ?? null), /*#__PURE__*/ react.createElement("div", {
17
+ className: "min-w-0 flex-1"
18
+ }, /*#__PURE__*/ react.createElement(Text, {
19
+ as: "div",
20
+ size: "md",
21
+ className: "truncate font-medium text-neutral-primary"
22
+ }, row.label), row.sub ? /*#__PURE__*/ react.createElement(Text, {
23
+ as: "div",
24
+ size: "sm",
25
+ className: "truncate text-neutral-muted"
26
+ }, row.sub) : null), /*#__PURE__*/ react.createElement("div", {
27
+ className: "ml-auto flex shrink-0 items-center gap-xs"
28
+ }, row.shortcut?.map((key, index)=>/*#__PURE__*/ react.createElement(Kbd, {
29
+ key: index
30
+ }, key)), selected ? /*#__PURE__*/ react.createElement("span", {
31
+ className: "inline-flex items-center gap-xs rounded-sm bg-primary px-xs py-xs text-sm font-medium text-neutral-base"
32
+ }, row.verb, /*#__PURE__*/ react.createElement(Icon, {
33
+ icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
34
+ color: "neutral-base",
35
+ size: "xs",
36
+ label: ""
37
+ })) : null));
38
+ };
39
+ export { CommandItemRow };
40
+
41
+ //# sourceMappingURL=CommandItemRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/CommandItemRow.js","sources":["../../../src/CommandPalette/components/CommandItemRow.tsx"],"sourcesContent":["import React from \"react\";\nimport { Command, useCommandState } from \"cmdk\";\nimport { cn } from \"@webiny/admin-ui\";\nimport { Icon } from \"@webiny/admin-ui\";\nimport { Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as ReturnIcon } from \"@webiny/icons/keyboard_return.svg\";\nimport type { CommandRow } from \"../types.js\";\nimport { Kbd } from \"./Kbd.js\";\n\nexport const CommandItemRow = ({ row }: { row: CommandRow }) => {\n const selected = useCommandState(state => (state.value || \"\") === row.value);\n\n let tile = \"border-neutral-dimmed bg-neutral-subtle\";\n if (selected) {\n tile = \"border-primary bg-primary-subtle\";\n }\n\n return (\n <Command.Item\n value={row.value}\n onSelect={row.onRun}\n className=\"flex cursor-pointer items-center gap-sm rounded-md px-sm py-xs-plus data-[selected=true]:bg-neutral-dimmed\"\n >\n <div className={cn(\"grid size-xl shrink-0 place-items-center rounded-md border\", tile)}>\n {row.icon ?? null}\n </div>\n <div className=\"min-w-0 flex-1\">\n <Text as=\"div\" size=\"md\" className=\"truncate font-medium text-neutral-primary\">\n {row.label}\n </Text>\n {row.sub ? (\n <Text as=\"div\" size=\"sm\" className=\"truncate text-neutral-muted\">\n {row.sub}\n </Text>\n ) : null}\n </div>\n <div className=\"ml-auto flex shrink-0 items-center gap-xs\">\n {row.shortcut?.map((key, index) => (\n <Kbd key={index}>{key}</Kbd>\n ))}\n {selected ? (\n <span className=\"inline-flex items-center gap-xs rounded-sm bg-primary px-xs py-xs text-sm font-medium text-neutral-base\">\n {row.verb}\n <Icon icon={<ReturnIcon />} color={\"neutral-base\"} size={\"xs\"} label={\"\"} />\n </span>\n ) : null}\n </div>\n </Command.Item>\n );\n};\n"],"names":["CommandItemRow","row","selected","useCommandState","state","tile","Command","cn","Text","key","index","Kbd","Icon","ReturnIcon"],"mappings":";;;;;AASO,MAAMA,iBAAiB,CAAC,EAAEC,GAAG,EAAuB;IACvD,MAAMC,WAAWC,gBAAgBC,CAAAA,QAAUA,AAAAA,CAAAA,MAAM,KAAK,IAAI,EAAC,MAAOH,IAAI,KAAK;IAE3E,IAAII,OAAO;IACX,IAAIH,UACAG,OAAO;IAGX,OAAO,WAAP,GACI,oBAACC,QAAQ,IAAI;QACT,OAAOL,IAAI,KAAK;QAChB,UAAUA,IAAI,KAAK;QACnB,WAAU;qBAEV,oBAAC;QAAI,WAAWM,GAAG,8DAA8DF;OAC5EJ,IAAI,IAAI,IAAI,qBAEjB,oBAAC;QAAI,WAAU;qBACX,oBAACO,MAAIA;QAAC,IAAG;QAAM,MAAK;QAAK,WAAU;OAC9BP,IAAI,KAAK,GAEbA,IAAI,GAAG,GAAG,WAAH,GACJ,oBAACO,MAAIA;QAAC,IAAG;QAAM,MAAK;QAAK,WAAU;OAC9BP,IAAI,GAAG,IAEZ,qBAER,oBAAC;QAAI,WAAU;OACVA,IAAI,QAAQ,EAAE,IAAI,CAACQ,KAAKC,QAAAA,WAAAA,GACrB,oBAACC,KAAGA;YAAC,KAAKD;WAAQD,OAErBP,WAAW,WAAXA,GACG,oBAAC;QAAK,WAAU;OACXD,IAAI,IAAI,gBACT,oBAACW,MAAIA;QAAC,oBAAM,oBAACC,gBAAUA;QAAK,OAAO;QAAgB,MAAM;QAAM,OAAO;UAE1E;AAIpB"}
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ export declare const GroupHeading: ({ title }: {
3
+ title: string;
4
+ }) => React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import react from "react";
2
+ const GroupHeading = ({ title })=>/*#__PURE__*/ react.createElement("span", {
3
+ className: "block px-sm pb-xs pt-sm text-xs font-semibold uppercase text-neutral-muted",
4
+ style: {
5
+ letterSpacing: ".06em"
6
+ }
7
+ }, title);
8
+ export { GroupHeading };
9
+
10
+ //# sourceMappingURL=GroupHeading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/GroupHeading.js","sources":["../../../src/CommandPalette/components/GroupHeading.tsx"],"sourcesContent":["import React from \"react\";\n\nexport const GroupHeading = ({ title }: { title: string }) => (\n <span\n className=\"block px-sm pb-xs pt-sm text-xs font-semibold uppercase text-neutral-muted\"\n style={{ letterSpacing: \".06em\" }}\n >\n {title}\n </span>\n);\n"],"names":["GroupHeading","title"],"mappings":";AAEO,MAAMA,eAAe,CAAC,EAAEC,KAAK,EAAqB,iBACrD,oBAAC;QACG,WAAU;QACV,OAAO;YAAE,eAAe;QAAQ;OAE/BA"}
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ export declare const HintIcon: ({ element }: {
3
+ element: React.ReactNode;
4
+ }) => React.JSX.Element;
@@ -0,0 +1,11 @@
1
+ import react from "react";
2
+ import { Icon } from "@webiny/admin-ui";
3
+ const HintIcon = ({ element })=>/*#__PURE__*/ react.createElement(Icon, {
4
+ icon: element,
5
+ color: "neutral-light",
6
+ size: "xs",
7
+ label: ""
8
+ });
9
+ export { HintIcon };
10
+
11
+ //# sourceMappingURL=HintIcon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/HintIcon.js","sources":["../../../src/CommandPalette/components/HintIcon.tsx"],"sourcesContent":["import React from \"react\";\nimport { Icon } from \"@webiny/admin-ui\";\n\nexport const HintIcon = ({ element }: { element: React.ReactNode }) => (\n <Icon icon={element} color={\"neutral-light\"} size={\"xs\"} label={\"\"} />\n);\n"],"names":["HintIcon","element","Icon"],"mappings":";;AAGO,MAAMA,WAAW,CAAC,EAAEC,OAAO,EAAgC,iBAC9D,oBAACC,MAAIA;QAAC,MAAMD;QAAS,OAAO;QAAiB,MAAM;QAAM,OAAO"}
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ export declare const Kbd: ({ children }: {
3
+ children: React.ReactNode;
4
+ }) => React.JSX.Element;
@@ -0,0 +1,14 @@
1
+ import react from "react";
2
+ const Kbd = ({ children })=>/*#__PURE__*/ react.createElement("span", {
3
+ className: "inline-flex items-center justify-center rounded-sm border border-neutral-dimmed bg-neutral-base text-xs text-neutral-muted",
4
+ style: {
5
+ minWidth: 20,
6
+ height: 20,
7
+ padding: "0 6px",
8
+ gap: 2,
9
+ fontFamily: "var(--font-family-mono, monospace)"
10
+ }
11
+ }, children);
12
+ export { Kbd };
13
+
14
+ //# sourceMappingURL=Kbd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/Kbd.js","sources":["../../../src/CommandPalette/components/Kbd.tsx"],"sourcesContent":["import React from \"react\";\n\nexport const Kbd = ({ children }: { children: React.ReactNode }) => (\n <span\n className=\"inline-flex items-center justify-center rounded-sm border border-neutral-dimmed bg-neutral-base text-xs text-neutral-muted\"\n style={{\n minWidth: 20,\n height: 20,\n padding: \"0 6px\",\n gap: 2,\n fontFamily: \"var(--font-family-mono, monospace)\"\n }}\n >\n {children}\n </span>\n);\n"],"names":["Kbd","children"],"mappings":";AAEO,MAAMA,MAAM,CAAC,EAAEC,QAAQ,EAAiC,iBAC3D,oBAAC;QACG,WAAU;QACV,OAAO;YACH,UAAU;YACV,QAAQ;YACR,SAAS;YACT,KAAK;YACL,YAAY;QAChB;OAECA"}
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ /**
3
+ * A dead end is the best moment to offer the assistant: the user has already expressed intent in
4
+ * words, so hand that exact query straight to AI rather than making them retype it.
5
+ */
6
+ export interface NoResultsProps {
7
+ query: string;
8
+ onAskAi: () => void;
9
+ }
10
+ export declare const NoResults: ({ query, onAskAi }: NoResultsProps) => React.JSX.Element;
@@ -0,0 +1,33 @@
1
+ import react from "react";
2
+ import { Button, Icon, Text } from "@webiny/admin-ui";
3
+ import { ReactComponent } from "@webiny/icons/search_off.svg";
4
+ import { ReactComponent as auto_awesome_svg_ReactComponent } from "@webiny/icons/auto_awesome.svg";
5
+ const NoResults = ({ query, onAskAi })=>/*#__PURE__*/ react.createElement("div", {
6
+ className: "flex flex-col items-center justify-center px-lg py-xxl text-center"
7
+ }, /*#__PURE__*/ react.createElement(Icon, {
8
+ icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
9
+ size: "lg",
10
+ label: "",
11
+ color: "neutral-strong-transparent"
12
+ }), /*#__PURE__*/ react.createElement(Text, {
13
+ as: "div",
14
+ size: "lg",
15
+ className: "mt-sm font-semibold text-neutral-strong"
16
+ }, `No results for “${query}”`), /*#__PURE__*/ react.createElement(Text, {
17
+ as: "div",
18
+ size: "sm",
19
+ className: "mb-md mt-xxs text-neutral-muted"
20
+ }, "No page or action matches. Ask the assistant instead."), /*#__PURE__*/ react.createElement(Button, {
21
+ variant: "secondary",
22
+ text: "Ask AI",
23
+ icon: /*#__PURE__*/ react.createElement(Icon, {
24
+ icon: /*#__PURE__*/ react.createElement(auto_awesome_svg_ReactComponent, null),
25
+ size: "sm",
26
+ label: "",
27
+ color: "inherit"
28
+ }),
29
+ onClick: onAskAi
30
+ }));
31
+ export { NoResults };
32
+
33
+ //# sourceMappingURL=NoResults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/NoResults.js","sources":["../../../src/CommandPalette/components/NoResults.tsx"],"sourcesContent":["import React from \"react\";\nimport { Button, Icon, Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as SearchOffIcon } from \"@webiny/icons/search_off.svg\";\nimport { ReactComponent as AiIcon } from \"@webiny/icons/auto_awesome.svg\";\n\n/**\n * A dead end is the best moment to offer the assistant: the user has already expressed intent in\n * words, so hand that exact query straight to AI rather than making them retype it.\n */\nexport interface NoResultsProps {\n query: string;\n onAskAi: () => void;\n}\n\nexport const NoResults = ({ query, onAskAi }: NoResultsProps) => (\n <div className=\"flex flex-col items-center justify-center px-lg py-xxl text-center\">\n <Icon icon={<SearchOffIcon />} size=\"lg\" label=\"\" color=\"neutral-strong-transparent\" />\n <Text as=\"div\" size=\"lg\" className=\"mt-sm font-semibold text-neutral-strong\">\n {`No results for “${query}”`}\n </Text>\n <Text as=\"div\" size=\"sm\" className=\"mb-md mt-xxs text-neutral-muted\">\n No page or action matches. Ask the assistant instead.\n </Text>\n <Button\n variant=\"secondary\"\n text=\"Ask AI\"\n icon={<Icon icon={<AiIcon />} size=\"sm\" label=\"\" color=\"inherit\" />}\n onClick={onAskAi}\n />\n </div>\n);\n"],"names":["NoResults","query","onAskAi","Icon","SearchOffIcon","Text","Button","AiIcon"],"mappings":";;;;AAcO,MAAMA,YAAY,CAAC,EAAEC,KAAK,EAAEC,OAAO,EAAkB,iBACxD,oBAAC;QAAI,WAAU;qBACX,oBAACC,MAAIA;QAAC,oBAAM,oBAACC,gBAAaA;QAAK,MAAK;QAAK,OAAM;QAAG,OAAM;sBACxD,oBAACC,MAAIA;QAAC,IAAG;QAAM,MAAK;QAAK,WAAU;OAC9B,CAAC,gBAAgB,EAAEJ,MAAM,CAAC,CAAC,iBAEhC,oBAACI,MAAIA;QAAC,IAAG;QAAM,MAAK;QAAK,WAAU;OAAkC,wEAGrE,oBAACC,QAAMA;QACH,SAAQ;QACR,MAAK;QACL,oBAAM,oBAACH,MAAIA;YAAC,oBAAM,oBAACI,iCAAMA;YAAK,MAAK;YAAK,OAAM;YAAG,OAAM;;QACvD,SAASL"}
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ export interface Hint {
3
+ keys: React.ReactNode;
4
+ label: string;
5
+ }
6
+ /**
7
+ * Hints are mode-specific: the shortcuts that matter while searching are not the ones that matter
8
+ * mid-conversation, and a static row would advertise keys that do nothing.
9
+ */
10
+ export declare const PaletteFooter: ({ label, hints }: {
11
+ label: string;
12
+ hints: Hint[];
13
+ }) => React.JSX.Element;
@@ -0,0 +1,18 @@
1
+ import react from "react";
2
+ import { Text } from "@webiny/admin-ui";
3
+ import { Kbd } from "./Kbd.js";
4
+ const PaletteFooter = ({ label, hints })=>/*#__PURE__*/ react.createElement("div", {
5
+ className: "flex flex-none items-center justify-between gap-sm border-t border-neutral-subtle bg-neutral-subtle px-sm py-xs-plus"
6
+ }, /*#__PURE__*/ react.createElement(Text, {
7
+ size: "sm",
8
+ className: "truncate text-neutral-muted"
9
+ }, label), /*#__PURE__*/ react.createElement("div", {
10
+ className: "flex shrink-0 items-center gap-md"
11
+ }, hints.map((hint)=>/*#__PURE__*/ react.createElement(Text, {
12
+ key: hint.label,
13
+ size: "sm",
14
+ className: "inline-flex items-center gap-xs text-neutral-muted"
15
+ }, /*#__PURE__*/ react.createElement(Kbd, null, hint.keys), hint.label))));
16
+ export { PaletteFooter };
17
+
18
+ //# sourceMappingURL=PaletteFooter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/PaletteFooter.js","sources":["../../../src/CommandPalette/components/PaletteFooter.tsx"],"sourcesContent":["import React from \"react\";\nimport { Text } from \"@webiny/admin-ui\";\nimport { Kbd } from \"./Kbd.js\";\n\nexport interface Hint {\n keys: React.ReactNode;\n label: string;\n}\n\n/**\n * Hints are mode-specific: the shortcuts that matter while searching are not the ones that matter\n * mid-conversation, and a static row would advertise keys that do nothing.\n */\nexport const PaletteFooter = ({ label, hints }: { label: string; hints: Hint[] }) => (\n <div className=\"flex flex-none items-center justify-between gap-sm border-t border-neutral-subtle bg-neutral-subtle px-sm py-xs-plus\">\n <Text size=\"sm\" className=\"truncate text-neutral-muted\">\n {label}\n </Text>\n <div className=\"flex shrink-0 items-center gap-md\">\n {hints.map(hint => (\n <Text\n key={hint.label}\n size=\"sm\"\n className=\"inline-flex items-center gap-xs text-neutral-muted\"\n >\n <Kbd>{hint.keys}</Kbd>\n {hint.label}\n </Text>\n ))}\n </div>\n </div>\n);\n"],"names":["PaletteFooter","label","hints","Text","hint","Kbd"],"mappings":";;;AAaO,MAAMA,gBAAgB,CAAC,EAAEC,KAAK,EAAEC,KAAK,EAAoC,iBAC5E,oBAAC;QAAI,WAAU;qBACX,oBAACC,MAAIA;QAAC,MAAK;QAAK,WAAU;OACrBF,QAAAA,WAAAA,GAEL,oBAAC;QAAI,WAAU;OACVC,MAAM,GAAG,CAACE,CAAAA,OAAAA,WAAAA,GACP,oBAACD,MAAIA;YACD,KAAKC,KAAK,KAAK;YACf,MAAK;YACL,WAAU;yBAEV,oBAACC,KAAGA,MAAED,KAAK,IAAI,GACdA,KAAK,KAAK"}
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ export interface ToolChipProps {
3
+ name: string;
4
+ /** Done chips read as confirmed work, failed ones as a call the model has to recover from. */
5
+ state: "running" | "done" | "failed";
6
+ }
7
+ export declare const ToolChip: ({ name, state }: ToolChipProps) => React.JSX.Element;
@@ -0,0 +1,32 @@
1
+ import react from "react";
2
+ import { Icon, Text, cn } from "@webiny/admin-ui";
3
+ import { ReactComponent } from "@webiny/icons/check.svg";
4
+ import { ReactComponent as autorenew_svg_ReactComponent } from "@webiny/icons/autorenew.svg";
5
+ import { ReactComponent as error_svg_ReactComponent } from "@webiny/icons/error.svg";
6
+ const toolIcon = (state)=>{
7
+ if ("done" === state) return /*#__PURE__*/ react.createElement(ReactComponent, null);
8
+ if ("failed" === state) return /*#__PURE__*/ react.createElement(error_svg_ReactComponent, null);
9
+ return /*#__PURE__*/ react.createElement(autorenew_svg_ReactComponent, null);
10
+ };
11
+ const ToolChip = ({ name, state })=>{
12
+ const done = "done" === state;
13
+ const failed = "failed" === state;
14
+ let tone = "border-neutral-dimmed bg-neutral-subtle text-neutral-strong";
15
+ if (done) tone = "border-success-subtle bg-success-subtle text-success";
16
+ else if (failed) tone = "border-destructive-subtle bg-destructive-subtle text-destructive-primary";
17
+ return /*#__PURE__*/ react.createElement("span", {
18
+ className: cn("inline-flex items-center gap-xs rounded-xl border px-sm py-xxs", tone)
19
+ }, /*#__PURE__*/ react.createElement(Icon, {
20
+ icon: toolIcon(state),
21
+ size: "xs",
22
+ label: "",
23
+ color: "inherit",
24
+ className: "running" === state ? "animate-spin" : void 0
25
+ }), /*#__PURE__*/ react.createElement(Text, {
26
+ size: "sm",
27
+ className: "font-mono"
28
+ }, name));
29
+ };
30
+ export { ToolChip };
31
+
32
+ //# sourceMappingURL=ToolChip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/components/ToolChip.js","sources":["../../../src/CommandPalette/components/ToolChip.tsx"],"sourcesContent":["import React from \"react\";\nimport { cn } from \"@webiny/admin-ui\";\nimport { Icon } from \"@webiny/admin-ui\";\nimport { Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as CheckIcon } from \"@webiny/icons/check.svg\";\nimport { ReactComponent as SpinnerIcon } from \"@webiny/icons/autorenew.svg\";\nimport { ReactComponent as ErrorIcon } from \"@webiny/icons/error.svg\";\n\nexport interface ToolChipProps {\n name: string;\n /** Done chips read as confirmed work, failed ones as a call the model has to recover from. */\n state: \"running\" | \"done\" | \"failed\";\n}\n\n/**\n * One tool the assistant called. Icon colour comes from the parent because `Icon` only offers\n * neutral/accent variants — `inherit` lets the chip's own text colour drive it.\n */\n/*\n * Lower-case on purpose: this picks an element, it is not a component. `Icon` does\n * `cloneElement(icon, { className })` to apply size, colour and the spin animation, so the SVG\n * element itself has to be what reaches it. Wrapping this in a component would hand those classes to\n * the wrapper, which drops them, and the chips would silently lose their styling.\n */\nconst toolIcon = (state: ToolChipProps[\"state\"]) => {\n if (state === \"done\") {\n return <CheckIcon />;\n }\n\n if (state === \"failed\") {\n return <ErrorIcon />;\n }\n\n return <SpinnerIcon />;\n};\n\nexport const ToolChip = ({ name, state }: ToolChipProps) => {\n const done = state === \"done\";\n const failed = state === \"failed\";\n\n let tone = \"border-neutral-dimmed bg-neutral-subtle text-neutral-strong\";\n if (done) {\n tone = \"border-success-subtle bg-success-subtle text-success\";\n } else if (failed) {\n tone = \"border-destructive-subtle bg-destructive-subtle text-destructive-primary\";\n }\n\n return (\n <span\n className={cn(\"inline-flex items-center gap-xs rounded-xl border px-sm py-xxs\", tone)}\n >\n <Icon\n icon={toolIcon(state)}\n size=\"xs\"\n label=\"\"\n color=\"inherit\"\n className={state === \"running\" ? \"animate-spin\" : undefined}\n />\n <Text size=\"sm\" className=\"font-mono\">\n {name}\n </Text>\n </span>\n );\n};\n"],"names":["toolIcon","state","CheckIcon","ErrorIcon","SpinnerIcon","ToolChip","name","done","failed","tone","cn","Icon","undefined","Text"],"mappings":";;;;;AAwBA,MAAMA,WAAW,CAACC;IACd,IAAIA,AAAU,WAAVA,OACA,OAAO,WAAP,GAAO,oBAACC,gBAASA;IAGrB,IAAID,AAAU,aAAVA,OACA,OAAO,WAAP,GAAO,oBAACE,0BAASA;IAGrB,OAAO,WAAP,GAAO,oBAACC,8BAAWA;AACvB;AAEO,MAAMC,WAAW,CAAC,EAAEC,IAAI,EAAEL,KAAK,EAAiB;IACnD,MAAMM,OAAON,AAAU,WAAVA;IACb,MAAMO,SAASP,AAAU,aAAVA;IAEf,IAAIQ,OAAO;IACX,IAAIF,MACAE,OAAO;SACJ,IAAID,QACPC,OAAO;IAGX,OAAO,WAAP,GACI,oBAAC;QACG,WAAWC,GAAG,kEAAkED;qBAEhF,oBAACE,MAAIA;QACD,MAAMX,SAASC;QACf,MAAK;QACL,OAAM;QACN,OAAM;QACN,WAAWA,AAAU,cAAVA,QAAsB,iBAAiBW;sBAEtD,oBAACC,MAAIA;QAAC,MAAK;QAAK,WAAU;OACrBP;AAIjB"}
@@ -0,0 +1,13 @@
1
+ export { CommandDetail } from "./CommandDetail.js";
2
+ export { CommandItemRow } from "./CommandItemRow.js";
3
+ export { GroupHeading } from "./GroupHeading.js";
4
+ export { HintIcon } from "./HintIcon.js";
5
+ export { Kbd } from "./Kbd.js";
6
+ export { AiModeBadge } from "./AiModeBadge.js";
7
+ export { AiSuggestions, AI_SUGGESTIONS } from "./AiSuggestions.js";
8
+ export { AiTurn } from "./AiTurn.js";
9
+ export { ApprovalPlan } from "./ApprovalPlan.js";
10
+ export { AnswerSkeleton } from "./AnswerSkeleton.js";
11
+ export { NoResults } from "./NoResults.js";
12
+ export { PaletteFooter, type Hint } from "./PaletteFooter.js";
13
+ export { ToolChip } from "./ToolChip.js";
@@ -0,0 +1,13 @@
1
+ export { CommandDetail } from "./CommandDetail.js";
2
+ export { CommandItemRow } from "./CommandItemRow.js";
3
+ export { GroupHeading } from "./GroupHeading.js";
4
+ export { HintIcon } from "./HintIcon.js";
5
+ export { Kbd } from "./Kbd.js";
6
+ export { AiModeBadge } from "./AiModeBadge.js";
7
+ export { AI_SUGGESTIONS, AiSuggestions } from "./AiSuggestions.js";
8
+ export { AiTurn } from "./AiTurn.js";
9
+ export { ApprovalPlan } from "./ApprovalPlan.js";
10
+ export { AnswerSkeleton } from "./AnswerSkeleton.js";
11
+ export { NoResults } from "./NoResults.js";
12
+ export { PaletteFooter } from "./PaletteFooter.js";
13
+ export { ToolChip } from "./ToolChip.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Dedicated hotkey layer for the command palette. `useHotkeys` only fires handlers
3
+ * registered on the current top-most zIndex, so the palette owns its own layer.
4
+ * NOTE: components that register a higher zIndex while open will shadow `mod+k`.
5
+ * Layering interplay is revisited in a later phase.
6
+ */
7
+ export declare const PALETTE_HOTKEY_ZINDEX = 90;
8
+ export declare const NAVIGATION_GROUP = "Navigation";
@@ -0,0 +1,5 @@
1
+ const PALETTE_HOTKEY_ZINDEX = 90;
2
+ const NAVIGATION_GROUP = "Navigation";
3
+ export { NAVIGATION_GROUP, PALETTE_HOTKEY_ZINDEX };
4
+
5
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/constants.js","sources":["../../src/CommandPalette/constants.ts"],"sourcesContent":["/**\n * Dedicated hotkey layer for the command palette. `useHotkeys` only fires handlers\n * registered on the current top-most zIndex, so the palette owns its own layer.\n * NOTE: components that register a higher zIndex while open will shadow `mod+k`.\n * Layering interplay is revisited in a later phase.\n */\nexport const PALETTE_HOTKEY_ZINDEX = 90;\n\nexport const NAVIGATION_GROUP = \"Navigation\";\n"],"names":["PALETTE_HOTKEY_ZINDEX","NAVIGATION_GROUP"],"mappings":"AAMO,MAAMA,wBAAwB;AAE9B,MAAMC,mBAAmB"}
@@ -0,0 +1,19 @@
1
+ import type { MenuConfig } from "@webiny/app-admin/config/AdminConfig/Menu.js";
2
+ import type { CommandItemVm } from "@webiny/app-admin";
3
+ import type { CommandGroup, CommandRow } from "./types.js";
4
+ /**
5
+ * Turn an is-hotkey shortcut string ("cmd+shift+m") into display chips (["⌘","⇧","M"]).
6
+ */
7
+ export declare const formatShortcut: (shortcut?: string) => string[] | undefined;
8
+ /**
9
+ * Derive Navigation rows from the registered admin menus. A navigable menu is a leaf
10
+ * `Menu.Link` (carries a `to` path + string `text`); `Menu.Item` parents/groups have
11
+ * no `to` and are skipped, but their label/icon are used for the child's section/icon.
12
+ */
13
+ export declare const deriveNavigationRows: (menus: MenuConfig[], navigateTo: (to: string) => void) => CommandRow[];
14
+ /**
15
+ * Group DI commands (from the presenter view model) by their `category` (default
16
+ * "Actions"), preserving order. Commands with a detail view show "Open" (the palette
17
+ * stays open on a sub-view); the rest show "Run".
18
+ */
19
+ export declare const commandVmsToGroups: (commands: CommandItemVm[], runCommand: (name: string) => void) => CommandGroup[];
@@ -0,0 +1,100 @@
1
+ import react from "react";
2
+ const DEFAULT_COMMAND_GROUP = "Actions";
3
+ const SHORTCUT_SYMBOLS = {
4
+ cmd: "⌘",
5
+ command: "⌘",
6
+ mod: "⌘",
7
+ ctrl: "⌃",
8
+ control: "⌃",
9
+ alt: "⌥",
10
+ option: "⌥",
11
+ shift: "⇧",
12
+ enter: "↵",
13
+ return: "↵"
14
+ };
15
+ const formatShortcut = (shortcut)=>{
16
+ if (!shortcut) return;
17
+ return shortcut.split("+").map((token)=>token.trim()).filter(Boolean).map((token)=>SHORTCUT_SYMBOLS[token.toLowerCase()] ?? token.toUpperCase());
18
+ };
19
+ const isNavigable = (element)=>{
20
+ if (!/*#__PURE__*/ react.isValidElement(element)) return false;
21
+ const props = element.props;
22
+ return "string" == typeof props.to && !!props.to && "string" == typeof props.text && !!props.text;
23
+ };
24
+ const deriveNavigationRows = (menus, navigateTo)=>{
25
+ const labelByName = new Map();
26
+ const iconByName = new Map();
27
+ const parentByName = new Map();
28
+ for (const menu of menus){
29
+ parentByName.set(menu.name, menu.parent ?? null);
30
+ const element = menu.element;
31
+ if (/*#__PURE__*/ react.isValidElement(element)) {
32
+ const props = element.props;
33
+ if ("string" == typeof props.text) labelByName.set(menu.name, props.text);
34
+ if (props.icon) iconByName.set(menu.name, props.icon);
35
+ }
36
+ }
37
+ const sectionFor = (name)=>{
38
+ const parts = [];
39
+ let parent = parentByName.get(name) ?? null;
40
+ for(let depth = 0; parent && depth < 10; depth++){
41
+ const label = labelByName.get(parent);
42
+ if (label) parts.unshift(label);
43
+ parent = parentByName.get(parent) ?? null;
44
+ }
45
+ return parts.length > 0 ? parts.join(" › ") : void 0;
46
+ };
47
+ const iconFor = (name, ownIcon)=>{
48
+ if (ownIcon) return ownIcon;
49
+ let parent = parentByName.get(name) ?? null;
50
+ for(let depth = 0; parent && depth < 10; depth++){
51
+ const icon = iconByName.get(parent);
52
+ if (icon) return icon;
53
+ parent = parentByName.get(parent) ?? null;
54
+ }
55
+ };
56
+ const rows = [];
57
+ const seenPaths = new Set();
58
+ for (const menu of menus){
59
+ if (!isNavigable(menu.element)) continue;
60
+ const { to, text, icon } = menu.element.props;
61
+ if (!seenPaths.has(to)) {
62
+ seenPaths.add(to);
63
+ rows.push({
64
+ key: menu.name,
65
+ value: `${text} ${menu.name}`.toLowerCase(),
66
+ label: text,
67
+ sub: sectionFor(menu.name),
68
+ icon: iconFor(menu.name, icon),
69
+ verb: "Open",
70
+ onRun: ()=>navigateTo(to)
71
+ });
72
+ }
73
+ }
74
+ return rows;
75
+ };
76
+ const commandVmsToGroups = (commands, runCommand)=>{
77
+ const groups = new Map();
78
+ for (const command of commands){
79
+ const category = command.category ?? DEFAULT_COMMAND_GROUP;
80
+ const rows = groups.get(category) ?? [];
81
+ rows.push({
82
+ key: command.name,
83
+ value: `${command.label} ${(command.keywords ?? []).join(" ")} ${command.name}`.toLowerCase(),
84
+ label: command.label,
85
+ sub: command.description,
86
+ icon: command.icon,
87
+ shortcut: formatShortcut(command.shortcut),
88
+ verb: command.hasDetailView ? "Open" : "Run",
89
+ onRun: ()=>runCommand(command.name)
90
+ });
91
+ groups.set(category, rows);
92
+ }
93
+ return Array.from(groups, ([title, rows])=>({
94
+ title,
95
+ rows
96
+ }));
97
+ };
98
+ export { commandVmsToGroups, deriveNavigationRows, formatShortcut };
99
+
100
+ //# sourceMappingURL=deriveRows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandPalette/deriveRows.js","sources":["../../src/CommandPalette/deriveRows.tsx"],"sourcesContent":["import React from \"react\";\nimport type { MenuConfig } from \"@webiny/app-admin/config/AdminConfig/Menu.js\";\nimport type { CommandItemVm } from \"@webiny/app-admin\";\nimport type { CommandGroup, CommandRow } from \"./types.js\";\n\nconst DEFAULT_COMMAND_GROUP = \"Actions\";\n\nconst SHORTCUT_SYMBOLS: Record<string, string> = {\n cmd: \"⌘\",\n command: \"⌘\",\n mod: \"⌘\",\n ctrl: \"⌃\",\n control: \"⌃\",\n alt: \"⌥\",\n option: \"⌥\",\n shift: \"⇧\",\n enter: \"↵\",\n return: \"↵\"\n};\n\n/**\n * Turn an is-hotkey shortcut string (\"cmd+shift+m\") into display chips ([\"⌘\",\"⇧\",\"M\"]).\n */\nexport const formatShortcut = (shortcut?: string): string[] | undefined => {\n if (!shortcut) {\n return undefined;\n }\n return shortcut\n .split(\"+\")\n .map(token => token.trim())\n .filter(Boolean)\n .map(token => SHORTCUT_SYMBOLS[token.toLowerCase()] ?? token.toUpperCase());\n};\n\nconst isNavigable = (\n element: unknown\n): element is React.ReactElement<{ to: string; text: string; icon?: React.ReactNode }> => {\n if (!React.isValidElement(element)) {\n return false;\n }\n const props = element.props as { to?: unknown; text?: unknown };\n return (\n typeof props.to === \"string\" && !!props.to && typeof props.text === \"string\" && !!props.text\n );\n};\n\n/**\n * Derive Navigation rows from the registered admin menus. A navigable menu is a leaf\n * `Menu.Link` (carries a `to` path + string `text`); `Menu.Item` parents/groups have\n * no `to` and are skipped, but their label/icon are used for the child's section/icon.\n */\nexport const deriveNavigationRows = (\n menus: MenuConfig[],\n navigateTo: (to: string) => void\n): CommandRow[] => {\n const labelByName = new Map<string, string>();\n const iconByName = new Map<string, React.ReactNode>();\n const parentByName = new Map<string, string | null>();\n for (const menu of menus) {\n parentByName.set(menu.name, menu.parent ?? null);\n const element = menu.element;\n if (React.isValidElement(element)) {\n const props = element.props as { text?: unknown; icon?: React.ReactNode };\n if (typeof props.text === \"string\") {\n labelByName.set(menu.name, props.text);\n }\n if (props.icon) {\n iconByName.set(menu.name, props.icon);\n }\n }\n }\n\n // Full ancestor breadcrumb (\"Settings › System\"), so same-named leaves under\n // different sections (e.g. the File Manager app vs. its settings page) are\n // distinguishable. Depth-guarded against malformed parent cycles.\n const sectionFor = (name: string): string | undefined => {\n const parts: string[] = [];\n let parent = parentByName.get(name) ?? null;\n for (let depth = 0; parent && depth < 10; depth++) {\n const label = labelByName.get(parent);\n if (label) {\n parts.unshift(label);\n }\n parent = parentByName.get(parent) ?? null;\n }\n return parts.length > 0 ? parts.join(\" › \") : undefined;\n };\n\n // Leaf items often carry no icon of their own; walk up to the nearest ancestor\n // that has one (a group may be icon-less, but the section root usually isn't).\n const iconFor = (name: string, ownIcon?: React.ReactNode): React.ReactNode | undefined => {\n if (ownIcon) {\n return ownIcon;\n }\n let parent = parentByName.get(name) ?? null;\n for (let depth = 0; parent && depth < 10; depth++) {\n const icon = iconByName.get(parent);\n if (icon) {\n return icon;\n }\n parent = parentByName.get(parent) ?? null;\n }\n return undefined;\n };\n\n const rows: CommandRow[] = [];\n const seenPaths = new Set<string>();\n for (const menu of menus) {\n if (!isNavigable(menu.element)) {\n continue;\n }\n const { to, text, icon } = menu.element.props;\n // Skip exact duplicate destinations (same path registered more than once).\n if (seenPaths.has(to)) {\n continue;\n }\n seenPaths.add(to);\n\n rows.push({\n key: menu.name,\n value: `${text} ${menu.name}`.toLowerCase(),\n label: text,\n sub: sectionFor(menu.name),\n icon: iconFor(menu.name, icon),\n verb: \"Open\",\n onRun: () => navigateTo(to)\n });\n }\n return rows;\n};\n\n/**\n * Group DI commands (from the presenter view model) by their `category` (default\n * \"Actions\"), preserving order. Commands with a detail view show \"Open\" (the palette\n * stays open on a sub-view); the rest show \"Run\".\n */\nexport const commandVmsToGroups = (\n commands: CommandItemVm[],\n runCommand: (name: string) => void\n): CommandGroup[] => {\n const groups = new Map<string, CommandRow[]>();\n for (const command of commands) {\n const category = command.category ?? DEFAULT_COMMAND_GROUP;\n const rows = groups.get(category) ?? [];\n rows.push({\n key: command.name,\n value: `${command.label} ${(command.keywords ?? []).join(\" \")} ${command.name}`.toLowerCase(),\n label: command.label,\n sub: command.description,\n icon: command.icon,\n shortcut: formatShortcut(command.shortcut),\n verb: command.hasDetailView ? \"Open\" : \"Run\",\n onRun: () => runCommand(command.name)\n });\n groups.set(category, rows);\n }\n return Array.from(groups, ([title, rows]) => ({ title, rows }));\n};\n"],"names":["DEFAULT_COMMAND_GROUP","SHORTCUT_SYMBOLS","formatShortcut","shortcut","token","Boolean","isNavigable","element","React","props","deriveNavigationRows","menus","navigateTo","labelByName","Map","iconByName","parentByName","menu","sectionFor","name","parts","parent","depth","label","undefined","iconFor","ownIcon","icon","rows","seenPaths","Set","to","text","commandVmsToGroups","commands","runCommand","groups","command","category","Array","title"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import type React from "react";
2
+ import type { Hint } from "../components/index.js";
3
+ /**
4
+ * What a mode needs from the palette while it is handling a key.
5
+ *
6
+ * The palette owns the query and the input element, because there is ONE input row across every
7
+ * mode and switching must not move or remount it. A mode reads and writes the query through this
8
+ * rather than holding its own.
9
+ */
10
+ export interface PaletteModeKeyContext {
11
+ query: string;
12
+ setQuery(value: string): void;
13
+ /** Leave this mode and return to the command list. */
14
+ exit(): void;
15
+ }
16
+ /** How the palette should present itself while this mode is active. */
17
+ export interface PaletteModeAppearance {
18
+ /** Rendered in the input row, ahead of the input itself. */
19
+ icon: React.ReactNode;
20
+ iconColor: "accent" | "neutral-light";
21
+ iconLabel: string;
22
+ /** Optional marker beside the icon, e.g. a mode name pill. */
23
+ badge?: React.ReactNode;
24
+ placeholder: string;
25
+ footerLabel: string;
26
+ hints: Hint[];
27
+ /**
28
+ * Taller panel. A conversation grows and wants the room; a command list does not, and a list
29
+ * stretched to full height looks broken when it holds three items.
30
+ */
31
+ tall: boolean;
32
+ }
33
+ /**
34
+ * One way the palette can behave.
35
+ *
36
+ * A mode supplies its body, its keys and how the palette should look while it is active; the palette
37
+ * owns the shell, the query and the input. The point of the split is that adding a mode does not
38
+ * mean editing the palette: everything AI lives in `createAiMode`, and the palette only knows this
39
+ * interface.
40
+ *
41
+ * `appearance` and `body` are read during render, so an implementation can serve them from getters
42
+ * over observable state and let a reactive palette re-render on its own.
43
+ */
44
+ export interface PaletteMode {
45
+ appearance: PaletteModeAppearance;
46
+ /** Rendered in the palette's scroll area, in place of the command list. */
47
+ body: React.ReactNode;
48
+ /**
49
+ * Enter the mode, optionally carrying what the user had already typed.
50
+ *
51
+ * The seed exists for ONE entry point: the "Ask AI" button in the no-results state. A command
52
+ * search that found nothing already holds a sentence the user typed, and the button turns it
53
+ * into the question instead of making them retype it. Every other way in (space on an empty
54
+ * query, `mod+k`, selecting the Ask AI command) passes nothing and leaves an empty input.
55
+ *
56
+ * The palette calls this; the mode decides what a seed means. Returning focus to the shared
57
+ * input is the palette's job, since the input is the palette's.
58
+ */
59
+ enter(seed?: string): void;
60
+ /** Drop any state. Called when the mode is left and when the palette closes. */
61
+ reset(): void;
62
+ /**
63
+ * Handle a key while this mode is active. Return true when the key was consumed, so the palette
64
+ * leaves it alone.
65
+ */
66
+ handleKey(event: React.KeyboardEvent, context: PaletteModeKeyContext): boolean;
67
+ /**
68
+ * Called after the palette renders this mode's body, with the scroll container it was rendered
69
+ * into. For a mode whose body grows, such as a conversation, this is where it keeps the newest
70
+ * content in view. The element belongs to the palette; a mode only reads or scrolls it.
71
+ */
72
+ afterRender?(scrollContainer: HTMLElement): void;
73
+ }
File without changes