@webiny/app-admin-ui 0.0.0-unstable.a4f3e4ea8b → 0.0.0-unstable.a85ef555b2
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/Breadcrumbs/Breadcrumbs.d.ts +14 -0
- package/Breadcrumbs/Breadcrumbs.js +74 -0
- package/Breadcrumbs/Breadcrumbs.js.map +1 -0
- package/CommandPalette/CommandPalette.d.ts +2 -2
- package/CommandPalette/CommandPalette.js +106 -75
- package/CommandPalette/CommandPalette.js.map +1 -1
- package/CommandPalette/components/AiModeBadge.d.ts +3 -0
- package/CommandPalette/components/AiModeBadge.js +11 -0
- package/CommandPalette/components/AiModeBadge.js.map +1 -0
- package/CommandPalette/components/AiSuggestions.d.ts +9 -0
- package/CommandPalette/components/AiSuggestions.js +32 -0
- package/CommandPalette/components/AiSuggestions.js.map +1 -0
- package/CommandPalette/components/AiTurn.d.ts +11 -0
- package/CommandPalette/components/AiTurn.js +61 -0
- package/CommandPalette/components/AiTurn.js.map +1 -0
- package/CommandPalette/components/AnswerSkeleton.d.ts +6 -0
- package/CommandPalette/components/AnswerSkeleton.js +20 -0
- package/CommandPalette/components/AnswerSkeleton.js.map +1 -0
- package/CommandPalette/components/ApprovalPlan.d.ts +16 -0
- package/CommandPalette/components/ApprovalPlan.js +56 -0
- package/CommandPalette/components/ApprovalPlan.js.map +1 -0
- package/CommandPalette/components/CommandItemRow.js +4 -2
- package/CommandPalette/components/CommandItemRow.js.map +1 -1
- package/CommandPalette/components/NoResults.d.ts +13 -0
- package/CommandPalette/components/NoResults.js +33 -0
- package/CommandPalette/components/NoResults.js.map +1 -0
- package/CommandPalette/components/PaletteFooter.d.ts +13 -0
- package/CommandPalette/components/PaletteFooter.js +18 -0
- package/CommandPalette/components/PaletteFooter.js.map +1 -0
- package/CommandPalette/components/ToolChip.d.ts +7 -0
- package/CommandPalette/components/ToolChip.js +32 -0
- package/CommandPalette/components/ToolChip.js.map +1 -0
- package/CommandPalette/components/index.d.ts +13 -5
- package/CommandPalette/components/index.js +13 -5
- package/CommandPalette/modes/PaletteMode.d.ts +73 -0
- package/CommandPalette/modes/PaletteMode.js +0 -0
- package/CommandPalette/modes/createAiMode.d.ts +13 -0
- package/CommandPalette/modes/createAiMode.js +80 -0
- package/CommandPalette/modes/createAiMode.js.map +1 -0
- package/CommandPalette/modes/index.d.ts +2 -0
- package/CommandPalette/modes/index.js +1 -0
- package/CommandPalette/usePaletteHotkeys.d.ts +16 -0
- package/CommandPalette/usePaletteHotkeys.js +38 -0
- package/CommandPalette/usePaletteHotkeys.js.map +1 -0
- package/Dashboard/Welcome.d.ts +4 -1
- package/Dashboard/Welcome.js +111 -24
- package/Dashboard/Welcome.js.map +1 -1
- package/Dashboard/components/DashboardToolbar.d.ts +11 -0
- package/Dashboard/components/DashboardToolbar.js +48 -0
- package/Dashboard/components/DashboardToolbar.js.map +1 -0
- package/Dashboard/components/addWidgetDrawer/AddToColumnMenu.d.ts +10 -0
- package/Dashboard/components/addWidgetDrawer/AddToColumnMenu.js +30 -0
- package/Dashboard/components/addWidgetDrawer/AddToColumnMenu.js.map +1 -0
- package/Dashboard/components/addWidgetDrawer/AddWidgetDrawer.d.ts +12 -0
- package/Dashboard/components/addWidgetDrawer/AddWidgetDrawer.js +61 -0
- package/Dashboard/components/addWidgetDrawer/AddWidgetDrawer.js.map +1 -0
- package/Dashboard/components/addWidgetDrawer/DrawerWidgetRow.d.ts +10 -0
- package/Dashboard/components/addWidgetDrawer/DrawerWidgetRow.js +38 -0
- package/Dashboard/components/addWidgetDrawer/DrawerWidgetRow.js.map +1 -0
- package/Dashboard/components/addWidgetDrawer/WidgetPreviewFrame.d.ts +7 -0
- package/Dashboard/components/addWidgetDrawer/WidgetPreviewFrame.js +44 -0
- package/Dashboard/components/addWidgetDrawer/WidgetPreviewFrame.js.map +1 -0
- package/Dashboard/components/addWidgetDrawer/WidgetSketch.d.ts +4 -0
- package/Dashboard/components/addWidgetDrawer/WidgetSketch.js +20 -0
- package/Dashboard/components/addWidgetDrawer/WidgetSketch.js.map +1 -0
- package/Dashboard/components/addWidgetDrawer/WidgetTabContent.d.ts +14 -0
- package/Dashboard/components/addWidgetDrawer/WidgetTabContent.js +76 -0
- package/Dashboard/components/addWidgetDrawer/WidgetTabContent.js.map +1 -0
- package/Dashboard/components/addWidgetDrawer/types.d.ts +10 -0
- package/Dashboard/components/addWidgetDrawer/types.js +0 -0
- package/Dashboard/components/dnd/ColumnCountControl.d.ts +9 -0
- package/Dashboard/components/dnd/ColumnCountControl.js +52 -0
- package/Dashboard/components/dnd/ColumnCountControl.js.map +1 -0
- package/Dashboard/components/dnd/ColumnEmptyState.d.ts +10 -0
- package/Dashboard/components/dnd/ColumnEmptyState.js +52 -0
- package/Dashboard/components/dnd/ColumnEmptyState.js.map +1 -0
- package/Dashboard/components/dnd/DashboardDragLayer.d.ts +6 -0
- package/Dashboard/components/dnd/DashboardDragLayer.js +28 -0
- package/Dashboard/components/dnd/DashboardDragLayer.js.map +1 -0
- package/Dashboard/components/dnd/DashboardWidgetCard.d.ts +13 -0
- package/Dashboard/components/dnd/DashboardWidgetCard.js +59 -0
- package/Dashboard/components/dnd/DashboardWidgetCard.js.map +1 -0
- package/Dashboard/components/dnd/DashboardWidgetColumn.d.ts +19 -0
- package/Dashboard/components/dnd/DashboardWidgetColumn.js +72 -0
- package/Dashboard/components/dnd/DashboardWidgetColumn.js.map +1 -0
- package/Dashboard/components/dnd/WidgetDropIndicator.d.ts +11 -0
- package/Dashboard/components/dnd/WidgetDropIndicator.js +29 -0
- package/Dashboard/components/dnd/WidgetDropIndicator.js.map +1 -0
- package/Dashboard/components/dnd/WidgetSlot.d.ts +6 -0
- package/Dashboard/components/dnd/WidgetSlot.js +9 -0
- package/Dashboard/components/dnd/WidgetSlot.js.map +1 -0
- package/Dashboard/components/dnd/dragItem.d.ts +6 -0
- package/Dashboard/components/dnd/dragItem.js +4 -0
- package/Dashboard/components/dnd/dragItem.js.map +1 -0
- package/Dashboard/components/dnd/useWidgetHosts.d.ts +1 -0
- package/Dashboard/components/dnd/useWidgetHosts.js +16 -0
- package/Dashboard/components/dnd/useWidgetHosts.js.map +1 -0
- package/Dashboard/components/layout/EditableColumns.d.ts +13 -0
- package/Dashboard/components/layout/EditableColumns.js +25 -0
- package/Dashboard/components/layout/EditableColumns.js.map +1 -0
- package/Dashboard/components/layout/StaticColumns.d.ts +8 -0
- package/Dashboard/components/layout/StaticColumns.js +20 -0
- package/Dashboard/components/layout/StaticColumns.js.map +1 -0
- package/Dashboard/components/layout/collapseColumns.d.ts +1 -0
- package/Dashboard/components/layout/collapseColumns.js +16 -0
- package/Dashboard/components/layout/collapseColumns.js.map +1 -0
- package/Dashboard/components/layout/layoutConstants.d.ts +2 -0
- package/Dashboard/components/layout/layoutConstants.js +5 -0
- package/Dashboard/components/layout/layoutConstants.js.map +1 -0
- package/Dashboard/components/layout/useColumnsThatFit.d.ts +6 -0
- package/Dashboard/components/layout/useColumnsThatFit.js +30 -0
- package/Dashboard/components/layout/useColumnsThatFit.js.map +1 -0
- package/Dashboard/components/layout/useWidgetPortals.d.ts +5 -0
- package/Dashboard/components/layout/useWidgetPortals.js +26 -0
- package/Dashboard/components/layout/useWidgetPortals.js.map +1 -0
- package/Dashboard/dashboardLayout/loadLayout/FetchDashboardLayoutUseCase.d.ts +12 -0
- package/Dashboard/dashboardLayout/loadLayout/FetchDashboardLayoutUseCase.js +19 -0
- package/Dashboard/dashboardLayout/loadLayout/FetchDashboardLayoutUseCase.js.map +1 -0
- package/Dashboard/dashboardLayout/loadLayout/GetCachedDashboardLayoutUseCase.d.ts +12 -0
- package/Dashboard/dashboardLayout/loadLayout/GetCachedDashboardLayoutUseCase.js +19 -0
- package/Dashboard/dashboardLayout/loadLayout/GetCachedDashboardLayoutUseCase.js.map +1 -0
- package/Dashboard/dashboardLayout/loadLayout/abstractions.d.ts +15 -0
- package/Dashboard/dashboardLayout/loadLayout/abstractions.js +6 -0
- package/Dashboard/dashboardLayout/loadLayout/abstractions.js.map +1 -0
- package/Dashboard/dashboardLayout/loadLayout/feature.d.ts +1 -0
- package/Dashboard/dashboardLayout/loadLayout/feature.js +13 -0
- package/Dashboard/dashboardLayout/loadLayout/feature.js.map +1 -0
- package/Dashboard/dashboardLayout/presenter/DashboardLayoutPresenter.d.ts +56 -0
- package/Dashboard/dashboardLayout/presenter/DashboardLayoutPresenter.js +308 -0
- package/Dashboard/dashboardLayout/presenter/DashboardLayoutPresenter.js.map +1 -0
- package/Dashboard/dashboardLayout/presenter/abstractions.d.ts +46 -0
- package/Dashboard/dashboardLayout/presenter/abstractions.js +5 -0
- package/Dashboard/dashboardLayout/presenter/abstractions.js.map +1 -0
- package/Dashboard/dashboardLayout/presenter/feature.d.ts +3 -0
- package/Dashboard/dashboardLayout/presenter/feature.js +23 -0
- package/Dashboard/dashboardLayout/presenter/feature.js.map +1 -0
- package/Dashboard/dashboardLayout/presenter/useDashboardLayoutPresenter.d.ts +1 -0
- package/Dashboard/dashboardLayout/presenter/useDashboardLayoutPresenter.js +6 -0
- package/Dashboard/dashboardLayout/presenter/useDashboardLayoutPresenter.js.map +1 -0
- package/Dashboard/dashboardLayout/repository/DashboardLayoutGateway.d.ts +13 -0
- package/Dashboard/dashboardLayout/repository/DashboardLayoutGateway.js +67 -0
- package/Dashboard/dashboardLayout/repository/DashboardLayoutGateway.js.map +1 -0
- package/Dashboard/dashboardLayout/repository/DashboardLayoutRepository.d.ts +20 -0
- package/Dashboard/dashboardLayout/repository/DashboardLayoutRepository.js +49 -0
- package/Dashboard/dashboardLayout/repository/DashboardLayoutRepository.js.map +1 -0
- package/Dashboard/dashboardLayout/repository/abstractions.d.ts +25 -0
- package/Dashboard/dashboardLayout/repository/abstractions.js +6 -0
- package/Dashboard/dashboardLayout/repository/abstractions.js.map +1 -0
- package/Dashboard/dashboardLayout/repository/feature.d.ts +1 -0
- package/Dashboard/dashboardLayout/repository/feature.js +13 -0
- package/Dashboard/dashboardLayout/repository/feature.js.map +1 -0
- package/Dashboard/dashboardLayout/saveLayout/SaveDashboardLayoutUseCase.d.ts +12 -0
- package/Dashboard/dashboardLayout/saveLayout/SaveDashboardLayoutUseCase.js +19 -0
- package/Dashboard/dashboardLayout/saveLayout/SaveDashboardLayoutUseCase.js.map +1 -0
- package/Dashboard/dashboardLayout/saveLayout/abstractions.d.ts +8 -0
- package/Dashboard/dashboardLayout/saveLayout/abstractions.js +5 -0
- package/Dashboard/dashboardLayout/saveLayout/abstractions.js.map +1 -0
- package/Dashboard/dashboardLayout/saveLayout/feature.d.ts +1 -0
- package/Dashboard/dashboardLayout/saveLayout/feature.js +11 -0
- package/Dashboard/dashboardLayout/saveLayout/feature.js.map +1 -0
- package/Dashboard/dashboardLayout/toColumnIndex.d.ts +1 -0
- package/Dashboard/dashboardLayout/toColumnIndex.js +7 -0
- package/Dashboard/dashboardLayout/toColumnIndex.js.map +1 -0
- package/Dashboard/dashboardLayout/types.d.ts +15 -0
- package/Dashboard/dashboardLayout/types.js +6 -0
- package/Dashboard/dashboardLayout/types.js.map +1 -0
- package/Layout.js +7 -5
- package/Layout.js.map +1 -1
- package/index.js +15 -4
- package/index.js.map +1 -1
- package/package.json +13 -7
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Header breadcrumbs. Reads the trail from the React Config API (`useAdminConfig().breadcrumbs`,
|
|
4
|
+
* populated by mounted `<Breadcrumb>` components), prepends the home entry, and renders the
|
|
5
|
+
* design-system primitive.
|
|
6
|
+
*
|
|
7
|
+
* Wrapped in `createReactiveComponent` so it re-renders on route changes too (it reads the
|
|
8
|
+
* router's current route) — config changes alone don't fire on every navigation.
|
|
9
|
+
*/
|
|
10
|
+
declare const BreadcrumbsBase: () => React.JSX.Element;
|
|
11
|
+
export declare const Breadcrumbs: typeof BreadcrumbsBase & React.NamedExoticComponent<unknown> & {
|
|
12
|
+
readonly type: typeof BreadcrumbsBase;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import react, { useCallback, useRef } from "react";
|
|
2
|
+
import { createReactiveComponent, useAdminConfig } from "@webiny/app-admin";
|
|
3
|
+
import { useContainer } from "@webiny/app";
|
|
4
|
+
import { RouterGateway, RouterPresenter } from "@webiny/app/features/router/abstractions.js";
|
|
5
|
+
import { Breadcrumbs, createHomeBreadcrumbItem } from "@webiny/admin-ui";
|
|
6
|
+
const HOME_PATH = "/";
|
|
7
|
+
const BreadcrumbsBase = ()=>{
|
|
8
|
+
const { breadcrumbs } = useAdminConfig();
|
|
9
|
+
const container = useContainer();
|
|
10
|
+
const currentRoute = container.resolve(RouterPresenter).vm.currentRoute;
|
|
11
|
+
const routeName = currentRoute?.name;
|
|
12
|
+
const onDashboard = currentRoute?.path === HOME_PATH;
|
|
13
|
+
const lastTrail = useRef({
|
|
14
|
+
items: []
|
|
15
|
+
});
|
|
16
|
+
let trail;
|
|
17
|
+
if (breadcrumbs.length > 0) {
|
|
18
|
+
lastTrail.current = {
|
|
19
|
+
routeName,
|
|
20
|
+
items: breadcrumbs
|
|
21
|
+
};
|
|
22
|
+
trail = breadcrumbs;
|
|
23
|
+
} else if (onDashboard) {
|
|
24
|
+
lastTrail.current = {
|
|
25
|
+
routeName,
|
|
26
|
+
items: []
|
|
27
|
+
};
|
|
28
|
+
trail = [];
|
|
29
|
+
} else trail = lastTrail.current.items;
|
|
30
|
+
const resolveLink = useCallback((to)=>{
|
|
31
|
+
if ("string" == typeof to) return to;
|
|
32
|
+
const router = container.resolve(RouterPresenter);
|
|
33
|
+
return to.params ? router.getLink(to.route, to.params) : router.getLink(to.route);
|
|
34
|
+
}, [
|
|
35
|
+
container
|
|
36
|
+
]);
|
|
37
|
+
const navigateTo = useCallback((to)=>{
|
|
38
|
+
container.resolve(RouterGateway).pushState(resolveLink(to));
|
|
39
|
+
}, [
|
|
40
|
+
container,
|
|
41
|
+
resolveLink
|
|
42
|
+
]);
|
|
43
|
+
const home = createHomeBreadcrumbItem(()=>navigateTo(HOME_PATH));
|
|
44
|
+
if (0 === trail.length) return /*#__PURE__*/ react.createElement(Breadcrumbs, {
|
|
45
|
+
items: [
|
|
46
|
+
home,
|
|
47
|
+
{
|
|
48
|
+
label: "Home",
|
|
49
|
+
current: true
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
});
|
|
53
|
+
const locations = trail.map((item, index)=>{
|
|
54
|
+
const isCurrent = index === trail.length - 1;
|
|
55
|
+
const to = item.to;
|
|
56
|
+
return {
|
|
57
|
+
label: item.label,
|
|
58
|
+
icon: item.icon,
|
|
59
|
+
title: item.label,
|
|
60
|
+
current: isCurrent,
|
|
61
|
+
onClick: !isCurrent && to ? ()=>navigateTo(to) : void 0
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
return /*#__PURE__*/ react.createElement(Breadcrumbs, {
|
|
65
|
+
items: [
|
|
66
|
+
home,
|
|
67
|
+
...locations
|
|
68
|
+
]
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
const Breadcrumbs_Breadcrumbs = createReactiveComponent(BreadcrumbsBase);
|
|
72
|
+
export { Breadcrumbs_Breadcrumbs as Breadcrumbs };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=Breadcrumbs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Breadcrumbs/Breadcrumbs.js","sources":["../../src/Breadcrumbs/Breadcrumbs.tsx"],"sourcesContent":["import React, { useCallback, useRef } from \"react\";\nimport { useAdminConfig, createReactiveComponent } from \"@webiny/app-admin\";\nimport type { BreadcrumbConfig, BreadcrumbLink } from \"@webiny/app-admin\";\nimport { useContainer } from \"@webiny/app\";\nimport { RouterGateway } from \"@webiny/app/features/router/abstractions.js\";\nimport { RouterPresenter } from \"@webiny/app/features/router/abstractions.js\";\nimport { Breadcrumbs as BreadcrumbsUI } from \"@webiny/admin-ui\";\nimport { createHomeBreadcrumbItem } from \"@webiny/admin-ui\";\nimport type { BreadcrumbsItem } from \"@webiny/admin-ui\";\n\nconst HOME_PATH = \"/\";\n\n/**\n * Header breadcrumbs. Reads the trail from the React Config API (`useAdminConfig().breadcrumbs`,\n * populated by mounted `<Breadcrumb>` components), prepends the home entry, and renders the\n * design-system primitive.\n *\n * Wrapped in `createReactiveComponent` so it re-renders on route changes too (it reads the\n * router's current route) — config changes alone don't fire on every navigation.\n */\nconst BreadcrumbsBase = () => {\n const { breadcrumbs } = useAdminConfig();\n const container = useContainer();\n\n // Reading `currentRoute` inside the reactive component subscribes to route changes, so the\n // header updates on every navigation — and lets us detect the (breadcrumb-less) dashboard.\n const currentRoute = container.resolve(RouterPresenter).vm.currentRoute;\n const routeName = currentRoute?.name;\n const onDashboard = currentRoute?.path === HOME_PATH;\n\n // Keep the previous trail visible while a newly-entered view loads its own (dynamic views\n // mount their breadcrumbs only after their data resolves, which would otherwise flash an\n // empty trail). Cleared on the dashboard, which is legitimately breadcrumb-less.\n const lastTrail = useRef<{ routeName?: string; items: BreadcrumbConfig[] }>({ items: [] });\n\n let trail: BreadcrumbConfig[];\n if (breadcrumbs.length > 0) {\n lastTrail.current = { routeName, items: breadcrumbs };\n trail = breadcrumbs;\n } else if (onDashboard) {\n lastTrail.current = { routeName, items: [] };\n trail = [];\n } else {\n // Empty trail on a non-dashboard route = the view is still loading its breadcrumbs;\n // hold the last known trail until they arrive.\n trail = lastTrail.current.items;\n }\n\n // Resolves a `to` (string or Route + params) to a concrete href.\n const resolveLink = useCallback(\n (to: BreadcrumbLink): string => {\n if (typeof to === \"string\") {\n return to;\n }\n const router = container.resolve(RouterPresenter);\n return to.params ? router.getLink(to.route, to.params) : router.getLink(to.route);\n },\n [container]\n );\n\n const navigateTo = useCallback(\n (to: BreadcrumbLink) => {\n container.resolve(RouterGateway).pushState(resolveLink(to));\n },\n [container, resolveLink]\n );\n\n // Leading home entry (icon) always navigates back to the dashboard.\n const home = createHomeBreadcrumbItem(() => navigateTo(HOME_PATH));\n\n // No trail (e.g. the dashboard) → show \"🏠 › Home\" rather than a lone, orphaned home icon\n // (or an empty bar after clicking home), keeping the same shape as trail pages.\n if (trail.length === 0) {\n return <BreadcrumbsUI items={[home, { label: \"Home\", current: true }]} />;\n }\n\n const locations = trail.map<BreadcrumbsItem>((item, index) => {\n const isCurrent = index === trail.length - 1;\n const to = item.to;\n return {\n label: item.label,\n icon: item.icon,\n title: item.label,\n current: isCurrent,\n onClick: !isCurrent && to ? () => navigateTo(to) : undefined\n };\n });\n\n return <BreadcrumbsUI items={[home, ...locations]} />;\n};\n\nexport const Breadcrumbs = createReactiveComponent(BreadcrumbsBase);\n"],"names":["HOME_PATH","BreadcrumbsBase","breadcrumbs","useAdminConfig","container","useContainer","currentRoute","RouterPresenter","routeName","onDashboard","lastTrail","useRef","trail","resolveLink","useCallback","to","router","navigateTo","RouterGateway","home","createHomeBreadcrumbItem","BreadcrumbsUI","locations","item","index","isCurrent","undefined","Breadcrumbs","createReactiveComponent"],"mappings":";;;;;AAUA,MAAMA,YAAY;AAUlB,MAAMC,kBAAkB;IACpB,MAAM,EAAEC,WAAW,EAAE,GAAGC;IACxB,MAAMC,YAAYC;IAIlB,MAAMC,eAAeF,UAAU,OAAO,CAACG,iBAAiB,EAAE,CAAC,YAAY;IACvE,MAAMC,YAAYF,cAAc;IAChC,MAAMG,cAAcH,cAAc,SAASN;IAK3C,MAAMU,YAAYC,OAA0D;QAAE,OAAO,EAAE;IAAC;IAExF,IAAIC;IACJ,IAAIV,YAAY,MAAM,GAAG,GAAG;QACxBQ,UAAU,OAAO,GAAG;YAAEF;YAAW,OAAON;QAAY;QACpDU,QAAQV;IACZ,OAAO,IAAIO,aAAa;QACpBC,UAAU,OAAO,GAAG;YAAEF;YAAW,OAAO,EAAE;QAAC;QAC3CI,QAAQ,EAAE;IACd,OAGIA,QAAQF,UAAU,OAAO,CAAC,KAAK;IAInC,MAAMG,cAAcC,YAChB,CAACC;QACG,IAAI,AAAc,YAAd,OAAOA,IACP,OAAOA;QAEX,MAAMC,SAASZ,UAAU,OAAO,CAACG;QACjC,OAAOQ,GAAG,MAAM,GAAGC,OAAO,OAAO,CAACD,GAAG,KAAK,EAAEA,GAAG,MAAM,IAAIC,OAAO,OAAO,CAACD,GAAG,KAAK;IACpF,GACA;QAACX;KAAU;IAGf,MAAMa,aAAaH,YACf,CAACC;QACGX,UAAU,OAAO,CAACc,eAAe,SAAS,CAACL,YAAYE;IAC3D,GACA;QAACX;QAAWS;KAAY;IAI5B,MAAMM,OAAOC,yBAAyB,IAAMH,WAAWjB;IAIvD,IAAIY,AAAiB,MAAjBA,MAAM,MAAM,EACZ,OAAO,WAAP,GAAO,oBAACS,aAAaA;QAAC,OAAO;YAACF;YAAM;gBAAE,OAAO;gBAAQ,SAAS;YAAK;SAAE;;IAGzE,MAAMG,YAAYV,MAAM,GAAG,CAAkB,CAACW,MAAMC;QAChD,MAAMC,YAAYD,UAAUZ,MAAM,MAAM,GAAG;QAC3C,MAAMG,KAAKQ,KAAK,EAAE;QAClB,OAAO;YACH,OAAOA,KAAK,KAAK;YACjB,MAAMA,KAAK,IAAI;YACf,OAAOA,KAAK,KAAK;YACjB,SAASE;YACT,SAAS,CAACA,aAAaV,KAAK,IAAME,WAAWF,MAAMW;QACvD;IACJ;IAEA,OAAO,WAAP,GAAO,oBAACL,aAAaA;QAAC,OAAO;YAACF;eAASG;SAAU;;AACrD;AAEO,MAAMK,0BAAcC,wBAAwB3B"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
declare const CommandPaletteBase: () => React.JSX.Element | null;
|
|
3
|
-
export declare const CommandPalette: typeof CommandPaletteBase & {
|
|
4
|
-
|
|
3
|
+
export declare const CommandPalette: typeof CommandPaletteBase & React.NamedExoticComponent<unknown> & {
|
|
4
|
+
readonly type: typeof CommandPaletteBase;
|
|
5
5
|
};
|
|
6
6
|
export {};
|
|
@@ -1,22 +1,50 @@
|
|
|
1
|
-
import react, { useCallback, useEffect, useMemo,
|
|
1
|
+
import react, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
2
|
import { Command } from "cmdk";
|
|
3
|
-
import { CommandPaletteFeature, createReactiveComponent, useAdminConfig,
|
|
3
|
+
import { AdminAssistantFeature, CommandPaletteFeature, createReactiveComponent, useAdminConfig, useFeatureFlags } from "@webiny/app-admin";
|
|
4
4
|
import { useContainer, useFeature } from "@webiny/app";
|
|
5
5
|
import { RouterGateway } from "@webiny/app/features/router/abstractions.js";
|
|
6
|
-
import {
|
|
6
|
+
import { Icon, ScrollArea } from "@webiny/admin-ui";
|
|
7
7
|
import { ReactComponent } from "@webiny/icons/search.svg";
|
|
8
8
|
import { ReactComponent as keyboard_return_svg_ReactComponent } from "@webiny/icons/keyboard_return.svg";
|
|
9
9
|
import { ReactComponent as keyboard_arrow_up_svg_ReactComponent } from "@webiny/icons/keyboard_arrow_up.svg";
|
|
10
10
|
import { ReactComponent as keyboard_arrow_down_svg_ReactComponent } from "@webiny/icons/keyboard_arrow_down.svg";
|
|
11
|
-
import { NAVIGATION_GROUP
|
|
11
|
+
import { NAVIGATION_GROUP } from "./constants.js";
|
|
12
12
|
import { commandVmsToGroups, deriveNavigationRows } from "./deriveRows.js";
|
|
13
|
-
import { CommandDetail, CommandItemRow, GroupHeading, HintIcon, Kbd } from "./components/index.js";
|
|
13
|
+
import { CommandDetail, CommandItemRow, GroupHeading, HintIcon, Kbd, NoResults, PaletteFooter } from "./components/index.js";
|
|
14
|
+
import { createAiMode } from "./modes/index.js";
|
|
15
|
+
import { usePaletteHotkeys } from "./usePaletteHotkeys.js";
|
|
16
|
+
const COMMAND_HINTS = [
|
|
17
|
+
{
|
|
18
|
+
keys: /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(HintIcon, {
|
|
19
|
+
element: /*#__PURE__*/ react.createElement(keyboard_arrow_up_svg_ReactComponent, null)
|
|
20
|
+
}), /*#__PURE__*/ react.createElement(HintIcon, {
|
|
21
|
+
element: /*#__PURE__*/ react.createElement(keyboard_arrow_down_svg_ReactComponent, null)
|
|
22
|
+
})),
|
|
23
|
+
label: "Navigate"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
keys: /*#__PURE__*/ react.createElement(HintIcon, {
|
|
27
|
+
element: /*#__PURE__*/ react.createElement(keyboard_return_svg_ReactComponent, null)
|
|
28
|
+
}),
|
|
29
|
+
label: "Select"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
keys: "space",
|
|
33
|
+
label: "Ask AI"
|
|
34
|
+
}
|
|
35
|
+
];
|
|
14
36
|
const CommandPaletteBase = ()=>{
|
|
15
|
-
const [query, setQuery] = useState("");
|
|
16
37
|
const { presenter } = useFeature(CommandPaletteFeature);
|
|
17
38
|
const { menus } = useAdminConfig();
|
|
18
39
|
const container = useContainer();
|
|
40
|
+
const inputRef = useRef(null);
|
|
41
|
+
const scrollRef = useRef(null);
|
|
19
42
|
const { vm } = presenter;
|
|
43
|
+
const { presenter: assistant } = useFeature(AdminAssistantFeature);
|
|
44
|
+
const aiMode = useMemo(()=>createAiMode(assistant), [
|
|
45
|
+
assistant
|
|
46
|
+
]);
|
|
47
|
+
const aiEnabled = useFeatureFlags().isEnabled("aiPowerups.adminAssistant");
|
|
20
48
|
useEffect(()=>{
|
|
21
49
|
presenter.init();
|
|
22
50
|
}, [
|
|
@@ -24,9 +52,17 @@ const CommandPaletteBase = ()=>{
|
|
|
24
52
|
]);
|
|
25
53
|
const close = useCallback(()=>{
|
|
26
54
|
presenter.close();
|
|
27
|
-
|
|
55
|
+
aiMode.reset();
|
|
28
56
|
}, [
|
|
29
|
-
presenter
|
|
57
|
+
presenter,
|
|
58
|
+
aiMode
|
|
59
|
+
]);
|
|
60
|
+
const exitAiMode = useCallback(()=>{
|
|
61
|
+
presenter.exitAiMode();
|
|
62
|
+
aiMode.reset();
|
|
63
|
+
}, [
|
|
64
|
+
presenter,
|
|
65
|
+
aiMode
|
|
30
66
|
]);
|
|
31
67
|
const navigateTo = useCallback((to)=>{
|
|
32
68
|
container.resolve(RouterGateway).pushState(to);
|
|
@@ -35,30 +71,10 @@ const CommandPaletteBase = ()=>{
|
|
|
35
71
|
container,
|
|
36
72
|
close
|
|
37
73
|
]);
|
|
38
|
-
|
|
39
|
-
presenter
|
|
40
|
-
]);
|
|
41
|
-
const keys = useMemo(()=>({
|
|
42
|
-
"mod+k": (e)=>{
|
|
43
|
-
e.preventDefault();
|
|
44
|
-
presenter.toggle();
|
|
45
|
-
setQuery("");
|
|
46
|
-
},
|
|
47
|
-
backspace: (e)=>{
|
|
48
|
-
if (!presenter.vm.isOpen) return;
|
|
49
|
-
const target = e.target;
|
|
50
|
-
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable) return;
|
|
51
|
-
e.preventDefault();
|
|
52
|
-
presenter.cancelCommand();
|
|
53
|
-
},
|
|
54
|
-
...presenter.shortcutKeys
|
|
55
|
-
}), [
|
|
74
|
+
usePaletteHotkeys({
|
|
56
75
|
presenter,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
useHotkeys({
|
|
60
|
-
zIndex: PALETTE_HOTKEY_ZINDEX,
|
|
61
|
-
keys
|
|
76
|
+
close,
|
|
77
|
+
aiEnabled
|
|
62
78
|
});
|
|
63
79
|
const groups = useMemo(()=>{
|
|
64
80
|
const result = [];
|
|
@@ -67,27 +83,58 @@ const CommandPaletteBase = ()=>{
|
|
|
67
83
|
title: NAVIGATION_GROUP,
|
|
68
84
|
rows: navigationRows
|
|
69
85
|
});
|
|
70
|
-
|
|
86
|
+
const commands = aiEnabled ? vm.commands : vm.commands.filter((cmd)=>!cmd.entersAiMode);
|
|
87
|
+
const commandGroups = commandVmsToGroups(commands, (name)=>presenter.useCommand(name));
|
|
88
|
+
result.push(...commandGroups);
|
|
71
89
|
return result;
|
|
72
90
|
}, [
|
|
73
91
|
menus,
|
|
74
92
|
vm.commands,
|
|
75
93
|
navigateTo,
|
|
76
|
-
|
|
94
|
+
presenter,
|
|
95
|
+
aiEnabled
|
|
96
|
+
]);
|
|
97
|
+
useEffect(()=>{
|
|
98
|
+
if (!vm.aiModeActive) return;
|
|
99
|
+
requestAnimationFrame(()=>inputRef.current?.focus());
|
|
100
|
+
}, [
|
|
101
|
+
vm.aiModeActive
|
|
77
102
|
]);
|
|
103
|
+
useEffect(()=>{
|
|
104
|
+
if (!vm.aiModeActive || !scrollRef.current) return;
|
|
105
|
+
aiMode.afterRender?.(scrollRef.current);
|
|
106
|
+
});
|
|
78
107
|
if (!vm.isOpen) return null;
|
|
79
108
|
const active = vm.activeCommand;
|
|
109
|
+
const askAiFromQuery = ()=>{
|
|
110
|
+
const seed = vm.query;
|
|
111
|
+
presenter.enterAiMode();
|
|
112
|
+
aiMode.enter(seed);
|
|
113
|
+
};
|
|
114
|
+
const appearance = vm.aiModeActive ? aiMode.appearance : null;
|
|
115
|
+
const placeholder = appearance ? appearance.placeholder : "Search for pages and actions…";
|
|
80
116
|
const onKeyDown = (e)=>{
|
|
81
117
|
if ("Escape" === e.key) {
|
|
82
118
|
e.preventDefault();
|
|
83
119
|
if (active) presenter.cancelCommand();
|
|
120
|
+
else if (vm.aiModeActive) exitAiMode();
|
|
84
121
|
else close();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (vm.aiModeActive) return void aiMode.handleKey(e, {
|
|
125
|
+
query: vm.query,
|
|
126
|
+
setQuery: (q)=>presenter.setQuery(q),
|
|
127
|
+
exit: exitAiMode
|
|
128
|
+
});
|
|
129
|
+
if (" " === e.key && "" === vm.query && aiEnabled) {
|
|
130
|
+
e.preventDefault();
|
|
131
|
+
presenter.enterAiMode();
|
|
85
132
|
}
|
|
86
133
|
};
|
|
87
134
|
return /*#__PURE__*/ react.createElement("div", {
|
|
88
135
|
role: "presentation",
|
|
89
136
|
onClick: close,
|
|
90
|
-
className: "fixed inset-0 z-overlay flex items-start justify-center bg-neutral-dark/50",
|
|
137
|
+
className: "fixed inset-0 z-overlay flex animate-in items-start justify-center bg-neutral-dark/50 fade-in duration-150",
|
|
91
138
|
style: {
|
|
92
139
|
padding: "13vh 16px 16px",
|
|
93
140
|
backdropFilter: "blur(2px)"
|
|
@@ -95,10 +142,11 @@ const CommandPaletteBase = ()=>{
|
|
|
95
142
|
}, /*#__PURE__*/ react.createElement("div", {
|
|
96
143
|
onClick: (e)=>e.stopPropagation(),
|
|
97
144
|
onKeyDown: onKeyDown,
|
|
98
|
-
className: "flex w-full flex-col overflow-hidden rounded-lg border border-neutral-dimmed bg-neutral-base shadow-xxl",
|
|
145
|
+
className: "flex w-full animate-in flex-col overflow-hidden rounded-lg border border-neutral-dimmed bg-neutral-base shadow-xxl duration-150 zoom-in-95 slide-in-from-top-2",
|
|
99
146
|
style: {
|
|
100
|
-
maxWidth:
|
|
101
|
-
|
|
147
|
+
maxWidth: 680,
|
|
148
|
+
maxHeight: "70vh",
|
|
149
|
+
height: appearance?.tall ? "70vh" : "45vh"
|
|
102
150
|
}
|
|
103
151
|
}, active ? /*#__PURE__*/ react.createElement(CommandDetail, {
|
|
104
152
|
active: active,
|
|
@@ -106,6 +154,7 @@ const CommandPaletteBase = ()=>{
|
|
|
106
154
|
onClose: close
|
|
107
155
|
}) : /*#__PURE__*/ react.createElement(Command, {
|
|
108
156
|
label: "Command palette",
|
|
157
|
+
shouldFilter: !appearance,
|
|
109
158
|
style: {
|
|
110
159
|
display: "flex",
|
|
111
160
|
flexDirection: "column",
|
|
@@ -113,30 +162,27 @@ const CommandPaletteBase = ()=>{
|
|
|
113
162
|
minHeight: 0
|
|
114
163
|
}
|
|
115
164
|
}, /*#__PURE__*/ react.createElement("div", {
|
|
116
|
-
className: "flex items-center gap-sm border-b border-neutral-subtle px-md py-sm-plus"
|
|
165
|
+
className: "flex flex-none items-center gap-sm border-b border-neutral-subtle px-md py-sm-plus"
|
|
117
166
|
}, /*#__PURE__*/ react.createElement(Icon, {
|
|
118
|
-
icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
119
|
-
color: "neutral-light",
|
|
167
|
+
icon: appearance ? appearance.icon : /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
168
|
+
color: appearance ? appearance.iconColor : "neutral-light",
|
|
120
169
|
size: "md",
|
|
121
|
-
label: "Search"
|
|
122
|
-
}), /*#__PURE__*/ react.createElement(Command.Input, {
|
|
170
|
+
label: appearance ? appearance.iconLabel : "Search"
|
|
171
|
+
}), appearance?.badge ?? null, /*#__PURE__*/ react.createElement(Command.Input, {
|
|
172
|
+
ref: inputRef,
|
|
123
173
|
autoFocus: true,
|
|
124
|
-
value: query,
|
|
125
|
-
onValueChange: setQuery,
|
|
174
|
+
value: vm.query,
|
|
175
|
+
onValueChange: (q)=>presenter.setQuery(q),
|
|
126
176
|
spellCheck: false,
|
|
127
|
-
placeholder:
|
|
177
|
+
placeholder: placeholder,
|
|
128
178
|
className: "min-w-0 flex-1 border-0 bg-transparent text-lg text-neutral-primary outline-none"
|
|
129
|
-
}), /*#__PURE__*/ react.createElement(Kbd, null, "esc")), /*#__PURE__*/ react.createElement(
|
|
130
|
-
className: "
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}, /*#__PURE__*/ react.createElement(Command.Empty, null, /*#__PURE__*/ react.createElement(EmptyState, {
|
|
137
|
-
type: "select",
|
|
138
|
-
title: `No results for “${query}”`,
|
|
139
|
-
description: "Try a page name, or an action like “new entry”."
|
|
179
|
+
}), /*#__PURE__*/ react.createElement(Kbd, null, "esc")), /*#__PURE__*/ react.createElement(ScrollArea, {
|
|
180
|
+
className: "min-h-0 flex-1",
|
|
181
|
+
viewportClassName: "p-xs-plus",
|
|
182
|
+
viewportRef: scrollRef
|
|
183
|
+
}, appearance ? aiMode.body : /*#__PURE__*/ react.createElement(Command.List, null, /*#__PURE__*/ react.createElement(Command.Empty, null, /*#__PURE__*/ react.createElement(NoResults, {
|
|
184
|
+
query: vm.query,
|
|
185
|
+
onAskAi: aiEnabled ? askAiFromQuery : void 0
|
|
140
186
|
})), groups.map((group)=>/*#__PURE__*/ react.createElement(Command.Group, {
|
|
141
187
|
key: group.title,
|
|
142
188
|
heading: /*#__PURE__*/ react.createElement(GroupHeading, {
|
|
@@ -145,25 +191,10 @@ const CommandPaletteBase = ()=>{
|
|
|
145
191
|
}, group.rows.map((row)=>/*#__PURE__*/ react.createElement(CommandItemRow, {
|
|
146
192
|
key: row.key,
|
|
147
193
|
row: row
|
|
148
|
-
})))))), /*#__PURE__*/ react.createElement(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}, "Webiny command palette"), /*#__PURE__*/ react.createElement("div", {
|
|
153
|
-
className: "flex items-center gap-md"
|
|
154
|
-
}, /*#__PURE__*/ react.createElement(Text, {
|
|
155
|
-
size: "sm",
|
|
156
|
-
className: "inline-flex items-center gap-xs"
|
|
157
|
-
}, /*#__PURE__*/ react.createElement(Kbd, null, /*#__PURE__*/ react.createElement(HintIcon, {
|
|
158
|
-
element: /*#__PURE__*/ react.createElement(keyboard_arrow_up_svg_ReactComponent, null)
|
|
159
|
-
}), /*#__PURE__*/ react.createElement(HintIcon, {
|
|
160
|
-
element: /*#__PURE__*/ react.createElement(keyboard_arrow_down_svg_ReactComponent, null)
|
|
161
|
-
})), "Navigate"), /*#__PURE__*/ react.createElement(Text, {
|
|
162
|
-
size: "sm",
|
|
163
|
-
className: "inline-flex items-center gap-xs"
|
|
164
|
-
}, /*#__PURE__*/ react.createElement(Kbd, null, /*#__PURE__*/ react.createElement(HintIcon, {
|
|
165
|
-
element: /*#__PURE__*/ react.createElement(keyboard_return_svg_ReactComponent, null)
|
|
166
|
-
})), "Select")))));
|
|
194
|
+
})))))), /*#__PURE__*/ react.createElement(PaletteFooter, {
|
|
195
|
+
label: appearance ? appearance.footerLabel : "Webiny command palette",
|
|
196
|
+
hints: appearance ? appearance.hints : COMMAND_HINTS
|
|
197
|
+
}))));
|
|
167
198
|
};
|
|
168
199
|
const CommandPalette = createReactiveComponent(CommandPaletteBase);
|
|
169
200
|
export { CommandPalette };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPalette/CommandPalette.js","sources":["../../src/CommandPalette/CommandPalette.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Command } from \"cmdk\";\nimport {\n CommandPaletteFeature,\n createReactiveComponent,\n useAdminConfig,\n useHotkeys\n} from \"@webiny/app-admin\";\nimport { useContainer, useFeature } from \"@webiny/app\";\nimport { RouterGateway } from \"@webiny/app/features/router/abstractions.js\";\nimport { EmptyState, Icon, Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as SearchIcon } from \"@webiny/icons/search.svg\";\nimport { ReactComponent as ReturnIcon } from \"@webiny/icons/keyboard_return.svg\";\nimport { ReactComponent as ArrowUpIcon } from \"@webiny/icons/keyboard_arrow_up.svg\";\nimport { ReactComponent as ArrowDownIcon } from \"@webiny/icons/keyboard_arrow_down.svg\";\nimport { NAVIGATION_GROUP, PALETTE_HOTKEY_ZINDEX } from \"./constants.js\";\nimport type { CommandGroup } from \"./types.js\";\nimport { commandVmsToGroups, deriveNavigationRows } from \"./deriveRows.js\";\nimport { CommandDetail, CommandItemRow, GroupHeading, HintIcon, Kbd } from \"./components/index.js\";\n\nconst CommandPaletteBase = () => {\n const [query, setQuery] = useState(\"\");\n const { presenter } = useFeature(CommandPaletteFeature);\n const { menus } = useAdminConfig();\n const container = useContainer();\n const { vm } = presenter;\n\n useEffect(() => {\n presenter.init();\n }, [presenter]);\n\n const close = useCallback(() => {\n presenter.close();\n setQuery(\"\");\n }, [presenter]);\n\n const navigateTo = useCallback(\n (to: string) => {\n container.resolve(RouterGateway).pushState(to);\n close();\n },\n [container, close]\n );\n\n const runCommand = useCallback((name: string) => presenter.useCommand(name), [presenter]);\n\n // mod+k toggles; backspace backs out of a detail view; command shortcuts run directly.\n const keys = useMemo(\n () => ({\n \"mod+k\": (e: KeyboardEvent) => {\n e.preventDefault();\n presenter.toggle();\n setQuery(\"\");\n },\n backspace: (e: KeyboardEvent) => {\n // This is a global (document-level) handler, so only act while the palette\n // is actually open — otherwise it would swallow Backspace everywhere.\n if (!presenter.vm.isOpen) {\n return;\n }\n // Never intercept Backspace while the user is editing text: <input>,\n // <textarea>, or any contentEditable (e.g. rich-text/lexical) element.\n const target = e.target;\n if (\n target instanceof HTMLInputElement ||\n target instanceof HTMLTextAreaElement ||\n (target instanceof HTMLElement && target.isContentEditable)\n ) {\n return;\n }\n e.preventDefault();\n presenter.cancelCommand();\n },\n ...presenter.shortcutKeys\n }),\n [presenter, presenter.shortcutKeys]\n );\n\n useHotkeys({ zIndex: PALETTE_HOTKEY_ZINDEX, keys });\n\n const groups = useMemo<CommandGroup[]>(() => {\n const result: CommandGroup[] = [];\n const navigationRows = deriveNavigationRows(menus, navigateTo);\n if (navigationRows.length > 0) {\n result.push({ title: NAVIGATION_GROUP, rows: navigationRows });\n }\n result.push(...commandVmsToGroups(vm.commands, runCommand));\n return result;\n }, [menus, vm.commands, navigateTo, runCommand]);\n\n if (!vm.isOpen) {\n return null;\n }\n\n const active = vm.activeCommand;\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n if (active) {\n presenter.cancelCommand();\n } else {\n close();\n }\n }\n };\n\n return (\n <div\n role=\"presentation\"\n onClick={close}\n className=\"fixed inset-0 z-overlay flex items-start justify-center bg-neutral-dark/50\"\n style={{ padding: \"13vh 16px 16px\", backdropFilter: \"blur(2px)\" }}\n >\n <div\n onClick={e => e.stopPropagation()}\n onKeyDown={onKeyDown}\n className=\"flex w-full flex-col overflow-hidden rounded-lg border border-neutral-dimmed bg-neutral-base shadow-xxl\"\n style={{ maxWidth: 660, height: \"45vh\" }}\n >\n {active ? (\n <CommandDetail\n active={active}\n onBack={() => presenter.cancelCommand()}\n onClose={close}\n />\n ) : (\n <Command\n label=\"Command palette\"\n style={{ display: \"flex\", flexDirection: \"column\", flex: 1, minHeight: 0 }}\n >\n <div className=\"flex items-center gap-sm border-b border-neutral-subtle px-md py-sm-plus\">\n <Icon\n icon={<SearchIcon />}\n color={\"neutral-light\"}\n size={\"md\"}\n label={\"Search\"}\n />\n <Command.Input\n autoFocus\n value={query}\n onValueChange={setQuery}\n spellCheck={false}\n placeholder=\"Search for pages and actions…\"\n className=\"min-w-0 flex-1 border-0 bg-transparent text-lg text-neutral-primary outline-none\"\n />\n <Kbd>esc</Kbd>\n </div>\n\n <Command.List\n className=\"p-xs-plus\"\n style={{ flex: 1, minHeight: 0, overflowY: \"auto\" }}\n >\n <Command.Empty>\n <EmptyState\n type=\"select\"\n title={`No results for “${query}”`}\n description=\"Try a page name, or an action like “new entry”.\"\n />\n </Command.Empty>\n\n {groups.map(group => (\n <Command.Group\n key={group.title}\n heading={<GroupHeading title={group.title} />}\n >\n {group.rows.map(row => (\n <CommandItemRow key={row.key} row={row} />\n ))}\n </Command.Group>\n ))}\n </Command.List>\n </Command>\n )}\n\n <div className=\"flex items-center justify-between gap-sm border-t border-neutral-subtle bg-neutral-subtle px-sm py-xs-plus text-neutral-muted\">\n <Text size=\"sm\">Webiny command palette</Text>\n <div className=\"flex items-center gap-md\">\n <Text size=\"sm\" className=\"inline-flex items-center gap-xs\">\n <Kbd>\n <HintIcon element={<ArrowUpIcon />} />\n <HintIcon element={<ArrowDownIcon />} />\n </Kbd>\n Navigate\n </Text>\n <Text size=\"sm\" className=\"inline-flex items-center gap-xs\">\n <Kbd>\n <HintIcon element={<ReturnIcon />} />\n </Kbd>\n Select\n </Text>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport const CommandPalette = createReactiveComponent(CommandPaletteBase);\n"],"names":["CommandPaletteBase","query","setQuery","useState","presenter","useFeature","CommandPaletteFeature","menus","useAdminConfig","container","useContainer","vm","useEffect","close","useCallback","navigateTo","to","RouterGateway","runCommand","name","keys","useMemo","e","target","HTMLInputElement","HTMLTextAreaElement","HTMLElement","useHotkeys","PALETTE_HOTKEY_ZINDEX","groups","result","navigationRows","deriveNavigationRows","NAVIGATION_GROUP","commandVmsToGroups","active","onKeyDown","CommandDetail","Command","Icon","SearchIcon","Kbd","EmptyState","group","GroupHeading","row","CommandItemRow","Text","HintIcon","ArrowUpIcon","ArrowDownIcon","ReturnIcon","CommandPalette","createReactiveComponent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommandPalette/CommandPalette.js","sources":["../../src/CommandPalette/CommandPalette.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { Command } from \"cmdk\";\nimport {\n AdminAssistantFeature,\n CommandPaletteFeature,\n createReactiveComponent,\n useAdminConfig,\n useFeatureFlags\n} from \"@webiny/app-admin\";\nimport { useContainer, useFeature } from \"@webiny/app\";\nimport { RouterGateway } from \"@webiny/app/features/router/abstractions.js\";\nimport { Icon, ScrollArea } from \"@webiny/admin-ui\";\nimport { ReactComponent as SearchIcon } from \"@webiny/icons/search.svg\";\nimport { ReactComponent as ReturnIcon } from \"@webiny/icons/keyboard_return.svg\";\nimport { ReactComponent as ArrowUpIcon } from \"@webiny/icons/keyboard_arrow_up.svg\";\nimport { ReactComponent as ArrowDownIcon } from \"@webiny/icons/keyboard_arrow_down.svg\";\nimport { NAVIGATION_GROUP } from \"./constants.js\";\nimport type { CommandGroup } from \"./types.js\";\nimport { commandVmsToGroups, deriveNavigationRows } from \"./deriveRows.js\";\nimport {\n CommandDetail,\n CommandItemRow,\n GroupHeading,\n HintIcon,\n Kbd,\n NoResults,\n PaletteFooter,\n type Hint\n} from \"./components/index.js\";\nimport { createAiMode } from \"./modes/index.js\";\nimport { usePaletteHotkeys } from \"./usePaletteHotkeys.js\";\n\nconst COMMAND_HINTS: Hint[] = [\n {\n keys: (\n <>\n <HintIcon element={<ArrowUpIcon />} />\n <HintIcon element={<ArrowDownIcon />} />\n </>\n ),\n label: \"Navigate\"\n },\n { keys: <HintIcon element={<ReturnIcon />} />, label: \"Select\" },\n { keys: \"space\", label: \"Ask AI\" }\n];\n\nconst CommandPaletteBase = () => {\n const { presenter } = useFeature(CommandPaletteFeature);\n const { menus } = useAdminConfig();\n const container = useContainer();\n const inputRef = useRef<HTMLInputElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const { vm } = presenter;\n\n /*\n * One mode today. The palette talks to it only through `PaletteMode`, so everything the\n * assistant needs lives in `createAiMode` rather than here. A second mode turns this into a\n * registry; nothing above this line has to change for that.\n */\n const { presenter: assistant } = useFeature(AdminAssistantFeature);\n const aiMode = useMemo(() => createAiMode(assistant), [assistant]);\n\n /*\n * The assistant is licensed separately, and the api registers no route without it. Checked here\n * rather than at registration because admin flags are fetched, so they are not known when the\n * container is built.\n */\n const aiEnabled = useFeatureFlags().isEnabled(\"aiPowerups.adminAssistant\");\n\n useEffect(() => {\n presenter.init();\n }, [presenter]);\n\n const close = useCallback(() => {\n presenter.close();\n aiMode.reset();\n }, [presenter, aiMode]);\n\n const exitAiMode = useCallback(() => {\n presenter.exitAiMode();\n aiMode.reset();\n }, [presenter, aiMode]);\n\n const navigateTo = useCallback(\n (to: string) => {\n container.resolve(RouterGateway).pushState(to);\n close();\n },\n [container, close]\n );\n\n usePaletteHotkeys({ presenter, close, aiEnabled });\n\n const groups = useMemo<CommandGroup[]>(() => {\n const result: CommandGroup[] = [];\n const navigationRows = deriveNavigationRows(menus, navigateTo);\n if (navigationRows.length > 0) {\n result.push({ title: NAVIGATION_GROUP, rows: navigationRows });\n }\n // Filtered on `entersAiMode` rather than on a command name, so a second mode needs no edit.\n const commands = aiEnabled ? vm.commands : vm.commands.filter(cmd => !cmd.entersAiMode);\n const commandGroups = commandVmsToGroups(commands, name => presenter.useCommand(name));\n result.push(...commandGroups);\n return result;\n }, [menus, vm.commands, navigateTo, presenter, aiEnabled]);\n\n /*\n * The input is shared across modes, so focus has to be restored after the surrounding tree swaps.\n * Driven by the presenter's state rather than by each entry point, since a mode can now also be\n * entered by selecting the command, which never reaches this component.\n */\n useEffect(() => {\n if (!vm.aiModeActive) {\n return;\n }\n requestAnimationFrame(() => inputRef.current?.focus());\n }, [vm.aiModeActive]);\n\n /*\n * Let the active mode keep its own content in view. Runs on every render while a mode is showing,\n * because the body is what changes and the palette cannot tell what inside it moved.\n */\n useEffect(() => {\n if (!vm.aiModeActive || !scrollRef.current) {\n return;\n }\n aiMode.afterRender?.(scrollRef.current);\n });\n\n if (!vm.isOpen) {\n return null;\n }\n\n const active = vm.activeCommand;\n\n /* The one seeded entry: a search that found nothing becomes the question as-is. */\n const askAiFromQuery = () => {\n // Read before entering, because entering clears the query.\n const seed = vm.query;\n presenter.enterAiMode();\n aiMode.enter(seed);\n };\n\n /* Null while the command list is showing, which is what every `appearance ?` below tests for. */\n const appearance = vm.aiModeActive ? aiMode.appearance : null;\n const placeholder = appearance ? appearance.placeholder : \"Search for pages and actions…\";\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n if (active) {\n presenter.cancelCommand();\n } else if (vm.aiModeActive) {\n exitAiMode();\n } else {\n close();\n }\n return;\n }\n\n if (vm.aiModeActive) {\n // The mode decides what its keys mean; anything it declines is simply ignored here.\n aiMode.handleKey(e, {\n query: vm.query,\n setQuery: q => presenter.setQuery(q),\n exit: exitAiMode\n });\n return;\n }\n\n // Space on an EMPTY query enters the aiMode. Gated on `query === \"\"` so space stays an ordinary\n // character the moment there is anything to search — \"new entry\" must keep working.\n if (e.key === \" \" && vm.query === \"\" && aiEnabled) {\n e.preventDefault();\n presenter.enterAiMode();\n }\n };\n\n return (\n <div\n role=\"presentation\"\n onClick={close}\n className=\"fixed inset-0 z-overlay flex animate-in items-start justify-center bg-neutral-dark/50 fade-in duration-150\"\n style={{ padding: \"13vh 16px 16px\", backdropFilter: \"blur(2px)\" }}\n >\n <div\n onClick={e => e.stopPropagation()}\n onKeyDown={onKeyDown}\n className=\"flex w-full animate-in flex-col overflow-hidden rounded-lg border border-neutral-dimmed bg-neutral-base shadow-xxl duration-150 zoom-in-95 slide-in-from-top-2\"\n style={{\n maxWidth: 680,\n maxHeight: \"70vh\",\n height: appearance?.tall ? \"70vh\" : \"45vh\"\n }}\n >\n {active ? (\n <CommandDetail\n active={active}\n onBack={() => presenter.cancelCommand()}\n onClose={close}\n />\n ) : (\n <Command\n label=\"Command palette\"\n // cmdk filtering is for the command list; a mode renders its own body.\n shouldFilter={!appearance}\n style={{ display: \"flex\", flexDirection: \"column\", flex: 1, minHeight: 0 }}\n >\n {/* Input row — the same slot in every mode, so switching never moves it. */}\n <div className=\"flex flex-none items-center gap-sm border-b border-neutral-subtle px-md py-sm-plus\">\n <Icon\n icon={appearance ? appearance.icon : <SearchIcon />}\n color={appearance ? appearance.iconColor : \"neutral-light\"}\n size={\"md\"}\n label={appearance ? appearance.iconLabel : \"Search\"}\n />\n {appearance?.badge ?? null}\n <Command.Input\n ref={inputRef}\n autoFocus\n value={vm.query}\n onValueChange={q => presenter.setQuery(q)}\n spellCheck={false}\n placeholder={placeholder}\n className=\"min-w-0 flex-1 border-0 bg-transparent text-lg text-neutral-primary outline-none\"\n />\n <Kbd>esc</Kbd>\n </div>\n\n {/*\n * The body scrolls inside a `ScrollArea`, so it shows the same scrollbar as\n * the rest of the admin instead of the operating system's.\n */}\n <ScrollArea\n className=\"min-h-0 flex-1\"\n viewportClassName=\"p-xs-plus\"\n viewportRef={scrollRef}\n >\n {appearance ? (\n aiMode.body\n ) : (\n <Command.List>\n <Command.Empty>\n <NoResults\n query={vm.query}\n onAskAi={aiEnabled ? askAiFromQuery : undefined}\n />\n </Command.Empty>\n\n {groups.map(group => (\n <Command.Group\n key={group.title}\n heading={<GroupHeading title={group.title} />}\n >\n {group.rows.map(row => (\n <CommandItemRow key={row.key} row={row} />\n ))}\n </Command.Group>\n ))}\n </Command.List>\n )}\n </ScrollArea>\n\n <PaletteFooter\n label={appearance ? appearance.footerLabel : \"Webiny command palette\"}\n hints={appearance ? appearance.hints : COMMAND_HINTS}\n />\n </Command>\n )}\n </div>\n </div>\n );\n};\n\nexport const CommandPalette = createReactiveComponent(CommandPaletteBase);\n"],"names":["COMMAND_HINTS","HintIcon","ArrowUpIcon","ArrowDownIcon","ReturnIcon","CommandPaletteBase","presenter","useFeature","CommandPaletteFeature","menus","useAdminConfig","container","useContainer","inputRef","useRef","scrollRef","vm","assistant","AdminAssistantFeature","aiMode","useMemo","createAiMode","aiEnabled","useFeatureFlags","useEffect","close","useCallback","exitAiMode","navigateTo","to","RouterGateway","usePaletteHotkeys","groups","result","navigationRows","deriveNavigationRows","NAVIGATION_GROUP","commands","cmd","commandGroups","commandVmsToGroups","name","requestAnimationFrame","active","askAiFromQuery","seed","appearance","placeholder","onKeyDown","e","q","CommandDetail","Command","Icon","SearchIcon","Kbd","ScrollArea","NoResults","undefined","group","GroupHeading","row","CommandItemRow","PaletteFooter","CommandPalette","createReactiveComponent"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { Text } from "@webiny/admin-ui";
|
|
3
|
+
const AiModeBadge = ()=>/*#__PURE__*/ react.createElement("span", {
|
|
4
|
+
className: "inline-flex shrink-0 items-center rounded-sm border border-accent-dimmed bg-primary-subtle px-xs py-xxs"
|
|
5
|
+
}, /*#__PURE__*/ react.createElement(Text, {
|
|
6
|
+
size: "sm",
|
|
7
|
+
className: "font-semibold text-primary"
|
|
8
|
+
}, "Ask AI"));
|
|
9
|
+
export { AiModeBadge };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=AiModeBadge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandPalette/components/AiModeBadge.js","sources":["../../../src/CommandPalette/components/AiModeBadge.tsx"],"sourcesContent":["import React from \"react\";\nimport { Text } from \"@webiny/admin-ui\";\n\n/** Sits in the input row so AI mode is unmistakable while the input itself never moves. */\nexport const AiModeBadge = () => (\n <span className=\"inline-flex shrink-0 items-center rounded-sm border border-accent-dimmed bg-primary-subtle px-xs py-xxs\">\n <Text size=\"sm\" className=\"font-semibold text-primary\">\n Ask AI\n </Text>\n </span>\n);\n"],"names":["AiModeBadge","Text"],"mappings":";;AAIO,MAAMA,cAAc,kBACvB,oBAAC;QAAK,WAAU;qBACZ,oBAACC,MAAIA;QAAC,MAAK;QAAK,WAAU;OAA6B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Shown before the first question. Concrete, clickable examples teach what the assistant can do far
|
|
4
|
+
* better than placeholder text — and every one of these is answerable with the read-only tools.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AI_SUGGESTIONS: string[];
|
|
7
|
+
export declare const AiSuggestions: ({ onAsk }: {
|
|
8
|
+
onAsk: (question: string) => void;
|
|
9
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { Icon, Text } from "@webiny/admin-ui";
|
|
3
|
+
import { ReactComponent } from "@webiny/icons/auto_awesome.svg";
|
|
4
|
+
import { GroupHeading } from "./GroupHeading.js";
|
|
5
|
+
const AI_SUGGESTIONS = [
|
|
6
|
+
"What content models does this project have?",
|
|
7
|
+
"Which products are on sale?",
|
|
8
|
+
"What fields does the product model have?",
|
|
9
|
+
"Show me the most recently edited entries"
|
|
10
|
+
];
|
|
11
|
+
const AiSuggestions = ({ onAsk })=>/*#__PURE__*/ react.createElement("div", null, /*#__PURE__*/ react.createElement(GroupHeading, {
|
|
12
|
+
title: "Suggested"
|
|
13
|
+
}), AI_SUGGESTIONS.map((suggestion)=>/*#__PURE__*/ react.createElement("button", {
|
|
14
|
+
key: suggestion,
|
|
15
|
+
type: "button",
|
|
16
|
+
onClick: ()=>onAsk(suggestion),
|
|
17
|
+
className: "flex w-full cursor-pointer items-center gap-sm rounded-md px-sm py-xs text-left hover:bg-neutral-dimmed"
|
|
18
|
+
}, /*#__PURE__*/ react.createElement("span", {
|
|
19
|
+
className: "grid size-lg shrink-0 place-items-center rounded-md border border-neutral-dimmed bg-neutral-subtle"
|
|
20
|
+
}, /*#__PURE__*/ react.createElement(Icon, {
|
|
21
|
+
icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
22
|
+
size: "sm",
|
|
23
|
+
label: "",
|
|
24
|
+
color: "neutral-light"
|
|
25
|
+
})), /*#__PURE__*/ react.createElement(Text, {
|
|
26
|
+
as: "div",
|
|
27
|
+
size: "md",
|
|
28
|
+
className: "min-w-0 flex-1 truncate text-neutral-primary"
|
|
29
|
+
}, suggestion))));
|
|
30
|
+
export { AI_SUGGESTIONS, AiSuggestions };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=AiSuggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandPalette/components/AiSuggestions.js","sources":["../../../src/CommandPalette/components/AiSuggestions.tsx"],"sourcesContent":["import React from \"react\";\nimport { Icon, Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as AiIcon } from \"@webiny/icons/auto_awesome.svg\";\nimport { GroupHeading } from \"./GroupHeading.js\";\n\n/**\n * Shown before the first question. Concrete, clickable examples teach what the assistant can do far\n * better than placeholder text — and every one of these is answerable with the read-only tools.\n */\nexport const AI_SUGGESTIONS = [\n \"What content models does this project have?\",\n \"Which products are on sale?\",\n \"What fields does the product model have?\",\n \"Show me the most recently edited entries\"\n];\n\nexport const AiSuggestions = ({ onAsk }: { onAsk: (question: string) => void }) => (\n <div>\n <GroupHeading title=\"Suggested\" />\n {AI_SUGGESTIONS.map(suggestion => (\n <button\n key={suggestion}\n type=\"button\"\n onClick={() => onAsk(suggestion)}\n className=\"flex w-full cursor-pointer items-center gap-sm rounded-md px-sm py-xs text-left hover:bg-neutral-dimmed\"\n >\n <span className=\"grid size-lg shrink-0 place-items-center rounded-md border border-neutral-dimmed bg-neutral-subtle\">\n <Icon icon={<AiIcon />} size=\"sm\" label=\"\" color=\"neutral-light\" />\n </span>\n <Text as=\"div\" size=\"md\" className=\"min-w-0 flex-1 truncate text-neutral-primary\">\n {suggestion}\n </Text>\n </button>\n ))}\n </div>\n);\n"],"names":["AI_SUGGESTIONS","AiSuggestions","onAsk","GroupHeading","suggestion","Icon","AiIcon","Text"],"mappings":";;;;AASO,MAAMA,iBAAiB;IAC1B;IACA;IACA;IACA;CACH;AAEM,MAAMC,gBAAgB,CAAC,EAAEC,KAAK,EAAyC,iBAC1E,oBAAC,2BACG,oBAACC,cAAYA;QAAC,OAAM;QACnBH,eAAe,GAAG,CAACI,CAAAA,aAAAA,WAAAA,GAChB,oBAAC;YACG,KAAKA;YACL,MAAK;YACL,SAAS,IAAMF,MAAME;YACrB,WAAU;yBAEV,oBAAC;YAAK,WAAU;yBACZ,oBAACC,MAAIA;YAAC,oBAAM,oBAACC,gBAAMA;YAAK,MAAK;YAAK,OAAM;YAAG,OAAM;2BAErD,oBAACC,MAAIA;YAAC,IAAG;YAAM,MAAK;YAAK,WAAU;WAC9BH"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { AiTurnViewModel } from "@webiny/app-admin";
|
|
3
|
+
export interface AiTurnProps {
|
|
4
|
+
turn: AiTurnViewModel;
|
|
5
|
+
/** Initials of the signed-in user, shown against their question. */
|
|
6
|
+
initials: string;
|
|
7
|
+
busy: boolean;
|
|
8
|
+
onApprove: () => void;
|
|
9
|
+
onReject: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const AiTurn: ({ turn, initials, busy, onApprove, onReject }: AiTurnProps) => React.JSX.Element;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { Markdown, Text } from "@webiny/admin-ui";
|
|
3
|
+
import { ToolChip } from "./ToolChip.js";
|
|
4
|
+
import { AnswerSkeleton } from "./AnswerSkeleton.js";
|
|
5
|
+
import { ApprovalPlan } from "./ApprovalPlan.js";
|
|
6
|
+
const toolState = (turn, index)=>{
|
|
7
|
+
const name = turn.tools[index];
|
|
8
|
+
const callsSoFar = turn.tools.slice(0, index + 1).filter((tool)=>tool === name).length;
|
|
9
|
+
const resultsSoFar = turn.completed.filter((tool)=>tool === name).length;
|
|
10
|
+
const failuresSoFar = turn.failed.filter((tool)=>tool === name).length;
|
|
11
|
+
if (resultsSoFar + failuresSoFar < callsSoFar) return "running";
|
|
12
|
+
return callsSoFar > resultsSoFar ? "failed" : "done";
|
|
13
|
+
};
|
|
14
|
+
const renderAnswer = (turn)=>{
|
|
15
|
+
if (turn.error) return /*#__PURE__*/ react.createElement(Text, {
|
|
16
|
+
as: "div",
|
|
17
|
+
size: "sm",
|
|
18
|
+
className: "text-destructive-primary"
|
|
19
|
+
}, turn.error);
|
|
20
|
+
if (!turn.text && !turn.settled) return /*#__PURE__*/ react.createElement(AnswerSkeleton, null);
|
|
21
|
+
if (turn.text) return /*#__PURE__*/ react.createElement(Markdown, {
|
|
22
|
+
size: "sm",
|
|
23
|
+
className: "text-neutral-strong"
|
|
24
|
+
}, turn.text);
|
|
25
|
+
if (turn.pendingApprovals.length > 0) return null;
|
|
26
|
+
return /*#__PURE__*/ react.createElement(Text, {
|
|
27
|
+
as: "div",
|
|
28
|
+
size: "sm",
|
|
29
|
+
className: "text-neutral-muted"
|
|
30
|
+
}, "No answer returned.");
|
|
31
|
+
};
|
|
32
|
+
const AiTurn = ({ turn, initials, busy, onApprove, onReject })=>/*#__PURE__*/ react.createElement("div", {
|
|
33
|
+
className: "mb-md"
|
|
34
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
35
|
+
className: "flex items-start gap-sm px-sm pb-sm"
|
|
36
|
+
}, /*#__PURE__*/ react.createElement("span", {
|
|
37
|
+
className: "mt-xxs grid size-md shrink-0 place-items-center rounded-xl bg-neutral-dimmed"
|
|
38
|
+
}, /*#__PURE__*/ react.createElement(Text, {
|
|
39
|
+
size: "sm",
|
|
40
|
+
className: "text-xs font-bold text-neutral-strong"
|
|
41
|
+
}, initials)), /*#__PURE__*/ react.createElement(Text, {
|
|
42
|
+
as: "div",
|
|
43
|
+
size: "md",
|
|
44
|
+
className: "font-semibold text-neutral-primary"
|
|
45
|
+
}, turn.question)), /*#__PURE__*/ react.createElement("div", {
|
|
46
|
+
className: "px-sm"
|
|
47
|
+
}, turn.tools.length > 0 ? /*#__PURE__*/ react.createElement("div", {
|
|
48
|
+
className: "mb-sm flex flex-wrap items-center gap-xs"
|
|
49
|
+
}, turn.tools.map((name, index)=>/*#__PURE__*/ react.createElement(ToolChip, {
|
|
50
|
+
key: `${name}-${index}`,
|
|
51
|
+
name: name,
|
|
52
|
+
state: toolState(turn, index)
|
|
53
|
+
}))) : null, renderAnswer(turn), turn.pendingApprovals.length > 0 ? /*#__PURE__*/ react.createElement(ApprovalPlan, {
|
|
54
|
+
approvals: turn.pendingApprovals,
|
|
55
|
+
busy: busy,
|
|
56
|
+
onApprove: onApprove,
|
|
57
|
+
onReject: onReject
|
|
58
|
+
}) : null));
|
|
59
|
+
export { AiTurn };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=AiTurn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandPalette/components/AiTurn.js","sources":["../../../src/CommandPalette/components/AiTurn.tsx"],"sourcesContent":["import React from \"react\";\nimport { Markdown } from \"@webiny/admin-ui\";\nimport { Text } from \"@webiny/admin-ui\";\nimport type { AiTurnViewModel } from \"@webiny/app-admin\";\nimport { ToolChip } from \"./ToolChip.js\";\nimport { AnswerSkeleton } from \"./AnswerSkeleton.js\";\nimport { ApprovalPlan } from \"./ApprovalPlan.js\";\n\n/**\n * Settled only once the tool actually returned or threw. A call awaiting approval still arrives as a\n * `tool-call`, so keying off position would show a write as completed when it has only been proposed.\n *\n * Outcomes are counted together, because a tool called twice can succeed once and fail once and the\n * chips are told apart only by their order.\n */\nconst toolState = (turn: AiTurnViewModel, index: number): \"running\" | \"done\" | \"failed\" => {\n const name = turn.tools[index];\n const callsSoFar = turn.tools.slice(0, index + 1).filter(tool => tool === name).length;\n const resultsSoFar = turn.completed.filter(tool => tool === name).length;\n const failuresSoFar = turn.failed.filter(tool => tool === name).length;\n\n if (resultsSoFar + failuresSoFar < callsSoFar) {\n return \"running\";\n }\n\n // The failures land last, so a call beyond the successful ones is one of them.\n return callsSoFar > resultsSoFar ? \"failed\" : \"done\";\n};\n\n/**\n * One of four things fills the answer slot, in priority order: the failure, the skeleton, the answer,\n * or a note that nothing came back.\n *\n * The skeleton shows only until the first token lands. Once text is arriving, the text itself is the\n * progress indicator, and swapping a skeleton in and out under it would flicker.\n *\n * A turn waiting on approval has produced no text yet and has not failed, which is not the same as\n * returning nothing, so it gets neither the note nor the skeleton.\n */\nconst renderAnswer = (turn: AiTurnViewModel) => {\n if (turn.error) {\n return (\n <Text as=\"div\" size=\"sm\" className=\"text-destructive-primary\">\n {turn.error}\n </Text>\n );\n }\n\n if (!turn.text && !turn.settled) {\n return <AnswerSkeleton />;\n }\n\n if (turn.text) {\n return (\n <Markdown size=\"sm\" className=\"text-neutral-strong\">\n {turn.text}\n </Markdown>\n );\n }\n\n if (turn.pendingApprovals.length > 0) {\n return null;\n }\n\n return (\n <Text as=\"div\" size=\"sm\" className=\"text-neutral-muted\">\n No answer returned.\n </Text>\n );\n};\n\nexport interface AiTurnProps {\n turn: AiTurnViewModel;\n /** Initials of the signed-in user, shown against their question. */\n initials: string;\n busy: boolean;\n onApprove: () => void;\n onReject: () => void;\n}\n\nexport const AiTurn = ({ turn, initials, busy, onApprove, onReject }: AiTurnProps) => {\n return (\n <div className=\"mb-md\">\n <div className=\"flex items-start gap-sm px-sm pb-sm\">\n <span className=\"mt-xxs grid size-md shrink-0 place-items-center rounded-xl bg-neutral-dimmed\">\n <Text size=\"sm\" className=\"text-xs font-bold text-neutral-strong\">\n {initials}\n </Text>\n </span>\n <Text as=\"div\" size=\"md\" className=\"font-semibold text-neutral-primary\">\n {turn.question}\n </Text>\n </div>\n\n <div className=\"px-sm\">\n {turn.tools.length > 0 ? (\n <div className=\"mb-sm flex flex-wrap items-center gap-xs\">\n {turn.tools.map((name, index) => (\n <ToolChip\n key={`${name}-${index}`}\n name={name}\n state={toolState(turn, index)}\n />\n ))}\n </div>\n ) : null}\n\n {renderAnswer(turn)}\n\n {turn.pendingApprovals.length > 0 ? (\n <ApprovalPlan\n approvals={turn.pendingApprovals}\n busy={busy}\n onApprove={onApprove}\n onReject={onReject}\n />\n ) : null}\n </div>\n </div>\n );\n};\n"],"names":["toolState","turn","index","name","callsSoFar","tool","resultsSoFar","failuresSoFar","renderAnswer","Text","AnswerSkeleton","Markdown","AiTurn","initials","busy","onApprove","onReject","ToolChip","ApprovalPlan"],"mappings":";;;;;AAeA,MAAMA,YAAY,CAACC,MAAuBC;IACtC,MAAMC,OAAOF,KAAK,KAAK,CAACC,MAAM;IAC9B,MAAME,aAAaH,KAAK,KAAK,CAAC,KAAK,CAAC,GAAGC,QAAQ,GAAG,MAAM,CAACG,CAAAA,OAAQA,SAASF,MAAM,MAAM;IACtF,MAAMG,eAAeL,KAAK,SAAS,CAAC,MAAM,CAACI,CAAAA,OAAQA,SAASF,MAAM,MAAM;IACxE,MAAMI,gBAAgBN,KAAK,MAAM,CAAC,MAAM,CAACI,CAAAA,OAAQA,SAASF,MAAM,MAAM;IAEtE,IAAIG,eAAeC,gBAAgBH,YAC/B,OAAO;IAIX,OAAOA,aAAaE,eAAe,WAAW;AAClD;AAYA,MAAME,eAAe,CAACP;IAClB,IAAIA,KAAK,KAAK,EACV,OAAO,WAAP,GACI,oBAACQ,MAAIA;QAAC,IAAG;QAAM,MAAK;QAAK,WAAU;OAC9BR,KAAK,KAAK;IAKvB,IAAI,CAACA,KAAK,IAAI,IAAI,CAACA,KAAK,OAAO,EAC3B,OAAO,WAAP,GAAO,oBAACS,gBAAcA;IAG1B,IAAIT,KAAK,IAAI,EACT,OAAO,WAAP,GACI,oBAACU,UAAQA;QAAC,MAAK;QAAK,WAAU;OACzBV,KAAK,IAAI;IAKtB,IAAIA,KAAK,gBAAgB,CAAC,MAAM,GAAG,GAC/B,OAAO;IAGX,OAAO,WAAP,GACI,oBAACQ,MAAIA;QAAC,IAAG;QAAM,MAAK;QAAK,WAAU;OAAqB;AAIhE;AAWO,MAAMG,SAAS,CAAC,EAAEX,IAAI,EAAEY,QAAQ,EAAEC,IAAI,EAAEC,SAAS,EAAEC,QAAQ,EAAe,GACtE,WAAP,GACI,oBAAC;QAAI,WAAU;qBACX,oBAAC;QAAI,WAAU;qBACX,oBAAC;QAAK,WAAU;qBACZ,oBAACP,MAAIA;QAAC,MAAK;QAAK,WAAU;OACrBI,YAAAA,WAAAA,GAGT,oBAACJ,MAAIA;QAAC,IAAG;QAAM,MAAK;QAAK,WAAU;OAC9BR,KAAK,QAAQ,kBAItB,oBAAC;QAAI,WAAU;OACVA,KAAK,KAAK,CAAC,MAAM,GAAG,IAAI,WAAJ,GACjB,oBAAC;QAAI,WAAU;OACVA,KAAK,KAAK,CAAC,GAAG,CAAC,CAACE,MAAMD,QAAAA,WAAAA,GACnB,oBAACe,UAAQA;YACL,KAAK,GAAGd,KAAK,CAAC,EAAED,OAAO;YACvB,MAAMC;YACN,OAAOH,UAAUC,MAAMC;eAInC,MAEHM,aAAaP,OAEbA,KAAK,gBAAgB,CAAC,MAAM,GAAG,IAAI,WAAJ,GAC5B,oBAACiB,cAAYA;QACT,WAAWjB,KAAK,gBAAgB;QAChC,MAAMa;QACN,WAAWC;QACX,UAAUC;SAEd"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Stands in for the answer while the request is in flight. Staggered widths read as prose rather
|
|
4
|
+
* than as a progress bar, which is honest: we cannot show real progress without streaming.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AnswerSkeleton: () => React.JSX.Element;
|