@webiny/app-admin-ui 6.4.11 → 6.6.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Breadcrumbs/Breadcrumbs.d.ts +14 -0
- package/Breadcrumbs/Breadcrumbs.js +74 -0
- package/Breadcrumbs/Breadcrumbs.js.map +1 -0
- package/CommandPalette/CommandPalette.d.ts +6 -0
- package/CommandPalette/CommandPalette.js +202 -0
- package/CommandPalette/CommandPalette.js.map +1 -0
- 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/CommandDetail.d.ts +13 -0
- package/CommandPalette/components/CommandDetail.js +39 -0
- package/CommandPalette/components/CommandDetail.js.map +1 -0
- package/CommandPalette/components/CommandItemRow.d.ts +5 -0
- package/CommandPalette/components/CommandItemRow.js +41 -0
- package/CommandPalette/components/CommandItemRow.js.map +1 -0
- package/CommandPalette/components/GroupHeading.d.ts +4 -0
- package/CommandPalette/components/GroupHeading.js +10 -0
- package/CommandPalette/components/GroupHeading.js.map +1 -0
- package/CommandPalette/components/HintIcon.d.ts +4 -0
- package/CommandPalette/components/HintIcon.js +11 -0
- package/CommandPalette/components/HintIcon.js.map +1 -0
- package/CommandPalette/components/Kbd.d.ts +4 -0
- package/CommandPalette/components/Kbd.js +14 -0
- package/CommandPalette/components/Kbd.js.map +1 -0
- package/CommandPalette/components/NoResults.d.ts +10 -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 -0
- package/CommandPalette/components/index.js +13 -0
- package/CommandPalette/constants.d.ts +8 -0
- package/CommandPalette/constants.js +5 -0
- package/CommandPalette/constants.js.map +1 -0
- package/CommandPalette/deriveRows.d.ts +19 -0
- package/CommandPalette/deriveRows.js +100 -0
- package/CommandPalette/deriveRows.js.map +1 -0
- 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/types.d.ts +15 -0
- package/CommandPalette/types.js +0 -0
- package/CommandPalette/usePaletteHotkeys.d.ts +14 -0
- package/CommandPalette/usePaletteHotkeys.js +35 -0
- package/CommandPalette/usePaletteHotkeys.js.map +1 -0
- package/Layout.js +48 -20
- package/Layout.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +8 -7
- package/Navigation/SidebarProvider.d.ts +0 -6
- package/Navigation/SidebarProvider.js +0 -26
- package/Navigation/SidebarProvider.js.map +0 -1
- package/Navigation.d.ts +0 -2
- package/Navigation.js +0 -10
- package/Navigation.js.map +0 -1
|
@@ -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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import react, { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { Command } from "cmdk";
|
|
3
|
+
import { AiChatFeature, CommandPaletteFeature, createReactiveComponent, useAdminConfig } from "@webiny/app-admin";
|
|
4
|
+
import { useContainer, useFeature } from "@webiny/app";
|
|
5
|
+
import { RouterGateway } from "@webiny/app/features/router/abstractions.js";
|
|
6
|
+
import { Icon } from "@webiny/admin-ui";
|
|
7
|
+
import { ReactComponent } from "@webiny/icons/search.svg";
|
|
8
|
+
import { ReactComponent as keyboard_return_svg_ReactComponent } from "@webiny/icons/keyboard_return.svg";
|
|
9
|
+
import { ReactComponent as keyboard_arrow_up_svg_ReactComponent } from "@webiny/icons/keyboard_arrow_up.svg";
|
|
10
|
+
import { ReactComponent as keyboard_arrow_down_svg_ReactComponent } from "@webiny/icons/keyboard_arrow_down.svg";
|
|
11
|
+
import { NAVIGATION_GROUP } from "./constants.js";
|
|
12
|
+
import { commandVmsToGroups, deriveNavigationRows } from "./deriveRows.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
|
+
];
|
|
36
|
+
const CommandPaletteBase = ()=>{
|
|
37
|
+
const { presenter } = useFeature(CommandPaletteFeature);
|
|
38
|
+
const { menus } = useAdminConfig();
|
|
39
|
+
const container = useContainer();
|
|
40
|
+
const inputRef = useRef(null);
|
|
41
|
+
const scrollRef = useRef(null);
|
|
42
|
+
const { vm } = presenter;
|
|
43
|
+
const { presenter: aiChat } = useFeature(AiChatFeature);
|
|
44
|
+
const aiMode = useMemo(()=>createAiMode(aiChat), [
|
|
45
|
+
aiChat
|
|
46
|
+
]);
|
|
47
|
+
useEffect(()=>{
|
|
48
|
+
presenter.init();
|
|
49
|
+
}, [
|
|
50
|
+
presenter
|
|
51
|
+
]);
|
|
52
|
+
const close = useCallback(()=>{
|
|
53
|
+
presenter.close();
|
|
54
|
+
aiMode.reset();
|
|
55
|
+
}, [
|
|
56
|
+
presenter,
|
|
57
|
+
aiMode
|
|
58
|
+
]);
|
|
59
|
+
const exitAiMode = useCallback(()=>{
|
|
60
|
+
presenter.exitAiMode();
|
|
61
|
+
aiMode.reset();
|
|
62
|
+
}, [
|
|
63
|
+
presenter,
|
|
64
|
+
aiMode
|
|
65
|
+
]);
|
|
66
|
+
const navigateTo = useCallback((to)=>{
|
|
67
|
+
container.resolve(RouterGateway).pushState(to);
|
|
68
|
+
close();
|
|
69
|
+
}, [
|
|
70
|
+
container,
|
|
71
|
+
close
|
|
72
|
+
]);
|
|
73
|
+
usePaletteHotkeys({
|
|
74
|
+
presenter,
|
|
75
|
+
close
|
|
76
|
+
});
|
|
77
|
+
const groups = useMemo(()=>{
|
|
78
|
+
const result = [];
|
|
79
|
+
const navigationRows = deriveNavigationRows(menus, navigateTo);
|
|
80
|
+
if (navigationRows.length > 0) result.push({
|
|
81
|
+
title: NAVIGATION_GROUP,
|
|
82
|
+
rows: navigationRows
|
|
83
|
+
});
|
|
84
|
+
const commandGroups = commandVmsToGroups(vm.commands, (name)=>presenter.useCommand(name));
|
|
85
|
+
result.push(...commandGroups);
|
|
86
|
+
return result;
|
|
87
|
+
}, [
|
|
88
|
+
menus,
|
|
89
|
+
vm.commands,
|
|
90
|
+
navigateTo,
|
|
91
|
+
presenter
|
|
92
|
+
]);
|
|
93
|
+
useEffect(()=>{
|
|
94
|
+
if (!vm.aiModeActive) return;
|
|
95
|
+
requestAnimationFrame(()=>inputRef.current?.focus());
|
|
96
|
+
}, [
|
|
97
|
+
vm.aiModeActive
|
|
98
|
+
]);
|
|
99
|
+
useEffect(()=>{
|
|
100
|
+
if (!vm.aiModeActive || !scrollRef.current) return;
|
|
101
|
+
aiMode.afterRender?.(scrollRef.current);
|
|
102
|
+
});
|
|
103
|
+
if (!vm.isOpen) return null;
|
|
104
|
+
const active = vm.activeCommand;
|
|
105
|
+
const askAiFromQuery = ()=>{
|
|
106
|
+
const seed = vm.query;
|
|
107
|
+
presenter.enterAiMode();
|
|
108
|
+
aiMode.enter(seed);
|
|
109
|
+
};
|
|
110
|
+
const appearance = vm.aiModeActive ? aiMode.appearance : null;
|
|
111
|
+
const placeholder = appearance ? appearance.placeholder : "Search for pages and actions…";
|
|
112
|
+
const onKeyDown = (e)=>{
|
|
113
|
+
if ("Escape" === e.key) {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
if (active) presenter.cancelCommand();
|
|
116
|
+
else if (vm.aiModeActive) exitAiMode();
|
|
117
|
+
else close();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (vm.aiModeActive) return void aiMode.handleKey(e, {
|
|
121
|
+
query: vm.query,
|
|
122
|
+
setQuery: (q)=>presenter.setQuery(q),
|
|
123
|
+
exit: exitAiMode
|
|
124
|
+
});
|
|
125
|
+
if (" " === e.key && "" === vm.query) {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
presenter.enterAiMode();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
131
|
+
role: "presentation",
|
|
132
|
+
onClick: close,
|
|
133
|
+
className: "fixed inset-0 z-overlay flex animate-in items-start justify-center bg-neutral-dark/50 fade-in duration-150",
|
|
134
|
+
style: {
|
|
135
|
+
padding: "13vh 16px 16px",
|
|
136
|
+
backdropFilter: "blur(2px)"
|
|
137
|
+
}
|
|
138
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
139
|
+
onClick: (e)=>e.stopPropagation(),
|
|
140
|
+
onKeyDown: onKeyDown,
|
|
141
|
+
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",
|
|
142
|
+
style: {
|
|
143
|
+
maxWidth: 680,
|
|
144
|
+
maxHeight: "70vh",
|
|
145
|
+
height: appearance?.tall ? "70vh" : "45vh"
|
|
146
|
+
}
|
|
147
|
+
}, active ? /*#__PURE__*/ react.createElement(CommandDetail, {
|
|
148
|
+
active: active,
|
|
149
|
+
onBack: ()=>presenter.cancelCommand(),
|
|
150
|
+
onClose: close
|
|
151
|
+
}) : /*#__PURE__*/ react.createElement(Command, {
|
|
152
|
+
label: "Command palette",
|
|
153
|
+
shouldFilter: !appearance,
|
|
154
|
+
style: {
|
|
155
|
+
display: "flex",
|
|
156
|
+
flexDirection: "column",
|
|
157
|
+
flex: 1,
|
|
158
|
+
minHeight: 0
|
|
159
|
+
}
|
|
160
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
161
|
+
className: "flex flex-none items-center gap-sm border-b border-neutral-subtle px-md py-sm-plus"
|
|
162
|
+
}, /*#__PURE__*/ react.createElement(Icon, {
|
|
163
|
+
icon: appearance ? appearance.icon : /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
164
|
+
color: appearance ? appearance.iconColor : "neutral-light",
|
|
165
|
+
size: "md",
|
|
166
|
+
label: appearance ? appearance.iconLabel : "Search"
|
|
167
|
+
}), appearance?.badge ?? null, /*#__PURE__*/ react.createElement(Command.Input, {
|
|
168
|
+
ref: inputRef,
|
|
169
|
+
autoFocus: true,
|
|
170
|
+
value: vm.query,
|
|
171
|
+
onValueChange: (q)=>presenter.setQuery(q),
|
|
172
|
+
spellCheck: false,
|
|
173
|
+
placeholder: placeholder,
|
|
174
|
+
className: "min-w-0 flex-1 border-0 bg-transparent text-lg text-neutral-primary outline-none"
|
|
175
|
+
}), /*#__PURE__*/ react.createElement(Kbd, null, "esc")), /*#__PURE__*/ react.createElement("div", {
|
|
176
|
+
ref: scrollRef,
|
|
177
|
+
className: "p-xs-plus",
|
|
178
|
+
style: {
|
|
179
|
+
flex: 1,
|
|
180
|
+
minHeight: 0,
|
|
181
|
+
overflowY: "auto"
|
|
182
|
+
}
|
|
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: askAiFromQuery
|
|
186
|
+
})), groups.map((group)=>/*#__PURE__*/ react.createElement(Command.Group, {
|
|
187
|
+
key: group.title,
|
|
188
|
+
heading: /*#__PURE__*/ react.createElement(GroupHeading, {
|
|
189
|
+
title: group.title
|
|
190
|
+
})
|
|
191
|
+
}, group.rows.map((row)=>/*#__PURE__*/ react.createElement(CommandItemRow, {
|
|
192
|
+
key: row.key,
|
|
193
|
+
row: row
|
|
194
|
+
})))))), /*#__PURE__*/ react.createElement(PaletteFooter, {
|
|
195
|
+
label: appearance ? appearance.footerLabel : "Webiny command palette",
|
|
196
|
+
hints: appearance ? appearance.hints : COMMAND_HINTS
|
|
197
|
+
}))));
|
|
198
|
+
};
|
|
199
|
+
const CommandPalette = createReactiveComponent(CommandPaletteBase);
|
|
200
|
+
export { CommandPalette };
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=CommandPalette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 AiChatFeature,\n CommandPaletteFeature,\n createReactiveComponent,\n useAdminConfig\n} from \"@webiny/app-admin\";\nimport { useContainer, useFeature } from \"@webiny/app\";\nimport { RouterGateway } from \"@webiny/app/features/router/abstractions.js\";\nimport { Icon } 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: aiChat } = useFeature(AiChatFeature);\n const aiMode = useMemo(() => createAiMode(aiChat), [aiChat]);\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 });\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 const commandGroups = commandVmsToGroups(vm.commands, name => presenter.useCommand(name));\n result.push(...commandGroups);\n return result;\n }, [menus, vm.commands, navigateTo, presenter]);\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 === \"\") {\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 <div\n ref={scrollRef}\n className=\"p-xs-plus\"\n style={{ flex: 1, minHeight: 0, overflowY: \"auto\" }}\n >\n {appearance ? (\n aiMode.body\n ) : (\n <Command.List>\n <Command.Empty>\n <NoResults query={vm.query} onAskAi={askAiFromQuery} />\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 </div>\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","aiChat","AiChatFeature","aiMode","useMemo","createAiMode","useEffect","close","useCallback","exitAiMode","navigateTo","to","RouterGateway","usePaletteHotkeys","groups","result","navigationRows","deriveNavigationRows","NAVIGATION_GROUP","commandGroups","commandVmsToGroups","name","requestAnimationFrame","active","askAiFromQuery","seed","appearance","placeholder","onKeyDown","e","q","CommandDetail","Command","Icon","SearchIcon","Kbd","NoResults","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;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { Skeleton } from "@webiny/admin-ui";
|
|
3
|
+
const AnswerSkeleton = ()=>/*#__PURE__*/ react.createElement("div", {
|
|
4
|
+
className: "flex flex-col gap-xs"
|
|
5
|
+
}, /*#__PURE__*/ react.createElement(Skeleton, {
|
|
6
|
+
type: "text",
|
|
7
|
+
size: "xs",
|
|
8
|
+
className: "w-[78%]"
|
|
9
|
+
}), /*#__PURE__*/ react.createElement(Skeleton, {
|
|
10
|
+
type: "text",
|
|
11
|
+
size: "xs",
|
|
12
|
+
className: "w-[92%]"
|
|
13
|
+
}), /*#__PURE__*/ react.createElement(Skeleton, {
|
|
14
|
+
type: "text",
|
|
15
|
+
size: "xs",
|
|
16
|
+
className: "w-[54%]"
|
|
17
|
+
}));
|
|
18
|
+
export { AnswerSkeleton };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=AnswerSkeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandPalette/components/AnswerSkeleton.js","sources":["../../../src/CommandPalette/components/AnswerSkeleton.tsx"],"sourcesContent":["import React from \"react\";\nimport { Skeleton } from \"@webiny/admin-ui\";\n\n/**\n * Stands in for the answer while the request is in flight. Staggered widths read as prose rather\n * than as a progress bar, which is honest: we cannot show real progress without streaming.\n */\nexport const AnswerSkeleton = () => (\n <div className=\"flex flex-col gap-xs\">\n <Skeleton type=\"text\" size=\"xs\" className=\"w-[78%]\" />\n <Skeleton type=\"text\" size=\"xs\" className=\"w-[92%]\" />\n <Skeleton type=\"text\" size=\"xs\" className=\"w-[54%]\" />\n </div>\n);\n"],"names":["AnswerSkeleton","Skeleton"],"mappings":";;AAOO,MAAMA,iBAAiB,kBAC1B,oBAAC;QAAI,WAAU;qBACX,oBAACC,UAAQA;QAAC,MAAK;QAAO,MAAK;QAAK,WAAU;sBAC1C,oBAACA,UAAQA;QAAC,MAAK;QAAO,MAAK;QAAK,WAAU;sBAC1C,oBAACA,UAAQA;QAAC,MAAK;QAAO,MAAK;QAAK,WAAU"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { AiChatPendingApproval } from "@webiny/app-admin";
|
|
3
|
+
export interface ApprovalPlanProps {
|
|
4
|
+
approvals: AiChatPendingApproval[];
|
|
5
|
+
busy: boolean;
|
|
6
|
+
onApprove: () => void;
|
|
7
|
+
onReject: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The confirm gate for a proposed change.
|
|
11
|
+
*
|
|
12
|
+
* Arguments are shown as raw JSON on purpose. A prose summary would be the assistant describing its
|
|
13
|
+
* own request — and the assistant is exactly what is not yet trusted here. What the user approves has
|
|
14
|
+
* to be what actually runs.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ApprovalPlan: ({ approvals, busy, onApprove, onReject }: ApprovalPlanProps) => React.JSX.Element | null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { Button, Icon, Text, cn } from "@webiny/admin-ui";
|
|
3
|
+
import { ReactComponent } from "@webiny/icons/bolt.svg";
|
|
4
|
+
import { ReactComponent as warning_svg_ReactComponent } from "@webiny/icons/warning.svg";
|
|
5
|
+
const ApprovalPlan = ({ approvals, busy, onApprove, onReject })=>{
|
|
6
|
+
if (0 === approvals.length) return null;
|
|
7
|
+
const destructive = approvals.some((approval)=>approval.destructive);
|
|
8
|
+
let heading = `Approve ${approvals.length} changes`;
|
|
9
|
+
if (1 === approvals.length) heading = `Approve: ${approvals[0].title ?? approvals[0].toolName}`;
|
|
10
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
11
|
+
className: cn("mt-sm overflow-hidden rounded-md border border-neutral-dimmed", destructive ? "bg-destructive-subtle" : "bg-warning-subtle")
|
|
12
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
13
|
+
className: "flex items-center gap-sm border-b border-neutral-dimmed px-sm py-xs-plus"
|
|
14
|
+
}, /*#__PURE__*/ react.createElement(Icon, {
|
|
15
|
+
icon: destructive ? /*#__PURE__*/ react.createElement(warning_svg_ReactComponent, null) : /*#__PURE__*/ react.createElement(ReactComponent, null),
|
|
16
|
+
size: "sm",
|
|
17
|
+
label: "",
|
|
18
|
+
color: "inherit",
|
|
19
|
+
className: destructive ? "text-destructive-primary" : void 0
|
|
20
|
+
}), /*#__PURE__*/ react.createElement(Text, {
|
|
21
|
+
size: "sm",
|
|
22
|
+
className: "font-semibold text-neutral-primary"
|
|
23
|
+
}, heading), destructive ? /*#__PURE__*/ react.createElement(Text, {
|
|
24
|
+
size: "sm",
|
|
25
|
+
className: "ml-auto text-destructive-primary"
|
|
26
|
+
}, "Cannot be undone") : null), /*#__PURE__*/ react.createElement("div", {
|
|
27
|
+
className: "bg-neutral-base px-sm py-xs-plus"
|
|
28
|
+
}, approvals.map((approval)=>/*#__PURE__*/ react.createElement("div", {
|
|
29
|
+
key: approval.approvalId,
|
|
30
|
+
className: "mb-xs"
|
|
31
|
+
}, /*#__PURE__*/ react.createElement(Text, {
|
|
32
|
+
as: "div",
|
|
33
|
+
size: "sm",
|
|
34
|
+
className: "font-mono text-neutral-strong"
|
|
35
|
+
}, approval.toolName), /*#__PURE__*/ react.createElement("pre", {
|
|
36
|
+
className: "mt-xxs overflow-x-auto rounded bg-neutral-subtle p-sm text-xs text-neutral-strong"
|
|
37
|
+
}, JSON.stringify(approval.input, null, 2)))), /*#__PURE__*/ react.createElement("div", {
|
|
38
|
+
className: "mt-sm flex items-center gap-sm"
|
|
39
|
+
}, /*#__PURE__*/ react.createElement(Button, {
|
|
40
|
+
variant: "primary",
|
|
41
|
+
text: "Run",
|
|
42
|
+
disabled: busy,
|
|
43
|
+
onClick: onApprove
|
|
44
|
+
}), /*#__PURE__*/ react.createElement(Button, {
|
|
45
|
+
variant: "secondary",
|
|
46
|
+
text: "Reject",
|
|
47
|
+
disabled: busy,
|
|
48
|
+
onClick: onReject
|
|
49
|
+
}), /*#__PURE__*/ react.createElement(Text, {
|
|
50
|
+
size: "sm",
|
|
51
|
+
className: "text-neutral-muted"
|
|
52
|
+
}, "Nothing runs until you confirm."))));
|
|
53
|
+
};
|
|
54
|
+
export { ApprovalPlan };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=ApprovalPlan.js.map
|