@keboola/design 11.6.0 → 11.9.0
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/AGENTS.md +16 -1
- package/CHANGELOG.md +92 -0
- package/COMPONENT-API.md +47 -46
- package/README.md +1 -1
- package/dist/{AIKit-BExvt9pt.js → AIKit-ELIsegJz.js} +551 -104
- package/dist/AIKit-ELIsegJz.js.map +1 -0
- package/dist/{Alert-Ba5b2YPq.js → Alert-BsIM_oFK.js} +2 -2
- package/dist/{Alert-Ba5b2YPq.js.map → Alert-BsIM_oFK.js.map} +1 -1
- package/dist/{Badge-D0pXdekt.js → Badge-o5jURtpB.js} +2 -2
- package/dist/{Badge-D0pXdekt.js.map → Badge-o5jURtpB.js.map} +1 -1
- package/dist/{BrandEditor-CSANsxUz.js → BrandEditor-BsTBM3s6.js} +5 -5
- package/dist/{BrandEditor-CSANsxUz.js.map → BrandEditor-BsTBM3s6.js.map} +1 -1
- package/dist/{Breadcrumb-g_xn_-Nf.js → Breadcrumb-xqEUxfwn.js} +2 -2
- package/dist/{Breadcrumb-g_xn_-Nf.js.map → Breadcrumb-xqEUxfwn.js.map} +1 -1
- package/dist/{Checkbox-CUIn2jap.js → Checkbox-Dq5fPNrO.js} +2 -2
- package/dist/{Checkbox-CUIn2jap.js.map → Checkbox-Dq5fPNrO.js.map} +1 -1
- package/dist/{Clipboard-BTW6C-b_.js → Clipboard-BZitKiSA.js} +2 -2
- package/dist/{Clipboard-BTW6C-b_.js.map → Clipboard-BZitKiSA.js.map} +1 -1
- package/dist/{ClipboardButton-CwZJdRaz.js → ClipboardButton-KHQnPEh_.js} +2 -2
- package/dist/{ClipboardButton-CwZJdRaz.js.map → ClipboardButton-KHQnPEh_.js.map} +1 -1
- package/dist/{CodeEditor-C9zy7PxK.js → CodeEditor-BJKJXA0c.js} +4 -4
- package/dist/{CodeEditor-C9zy7PxK.js.map → CodeEditor-BJKJXA0c.js.map} +1 -1
- package/dist/{ColorInput-BZV-u_94.js → ColorInput-Bj3dfPFA.js} +3 -3
- package/dist/{ColorInput-BZV-u_94.js.map → ColorInput-Bj3dfPFA.js.map} +1 -1
- package/dist/{ColorModeSwitch-CsWLiTBX.js → ColorModeSwitch-CWt-DZGC.js} +2 -2
- package/dist/{ColorModeSwitch-CsWLiTBX.js.map → ColorModeSwitch-CWt-DZGC.js.map} +1 -1
- package/dist/{Combobox-BfIfaVMA.js → Combobox-D8PrEqTG.js} +4 -4
- package/dist/{Combobox-BfIfaVMA.js.map → Combobox-D8PrEqTG.js.map} +1 -1
- package/dist/{Command-Bfa1kpbt.js → Command-BDU3C1gg.js} +2 -2
- package/dist/{Command-Bfa1kpbt.js.map → Command-BDU3C1gg.js.map} +1 -1
- package/dist/{CopyField-DpT0Ngz1.js → CopyField-BHRQUyuF.js} +2 -2
- package/dist/{CopyField-DpT0Ngz1.js.map → CopyField-BHRQUyuF.js.map} +1 -1
- package/dist/{DateInput-DiEd5uCL.js → DateInput-B6N8g0ra.js} +3 -3
- package/dist/{DateInput-DiEd5uCL.js.map → DateInput-B6N8g0ra.js.map} +1 -1
- package/dist/{DateRangeInput-DPfVYchl.js → DateRangeInput-7M9Z4cVO.js} +3 -3
- package/dist/{DateRangeInput-DPfVYchl.js.map → DateRangeInput-7M9Z4cVO.js.map} +1 -1
- package/dist/{DropdownMenu-CASr-jgl.js → DropdownMenu-CgVwhDvl.js} +9 -4
- package/dist/DropdownMenu-CgVwhDvl.js.map +1 -0
- package/dist/{FieldItem-B7XMmZSz.js → FieldItem-DZGIdiXE.js} +3 -3
- package/dist/{FieldItem-B7XMmZSz.js.map → FieldItem-DZGIdiXE.js.map} +1 -1
- package/dist/{FileUpload-ABThVZyg.js → FileUpload-DYQCTGjU.js} +3 -3
- package/dist/{FileUpload-ABThVZyg.js.map → FileUpload-DYQCTGjU.js.map} +1 -1
- package/dist/{Form-CsttTHn4.js → Form-CcK7dRrC.js} +5 -5
- package/dist/{Form-CsttTHn4.js.map → Form-CcK7dRrC.js.map} +1 -1
- package/dist/{FormGroup-DHvTFdno.js → FormGroup-DR_gZ25-.js} +3 -3
- package/dist/{FormGroup-DHvTFdno.js.map → FormGroup-DR_gZ25-.js.map} +1 -1
- package/dist/{InputGroup-DcZRlVnN.js → InputGroup-Do8K2hvD.js} +2 -1
- package/dist/InputGroup-Do8K2hvD.js.map +1 -0
- package/dist/{Kbd-Cvvt91Q8.js → Kbd-LCFMumoq.js} +2 -2
- package/dist/{Kbd-Cvvt91Q8.js.map → Kbd-LCFMumoq.js.map} +1 -1
- package/dist/{MermaidDiagram-DSI7xMu2.js → MermaidDiagram-ZG0iiDV_.js} +2 -2
- package/dist/{MermaidDiagram-DSI7xMu2.js.map → MermaidDiagram-ZG0iiDV_.js.map} +1 -1
- package/dist/{Modal-Coj-H02o.js → Modal-I3VWk2oo.js} +2 -2
- package/dist/{Modal-Coj-H02o.js.map → Modal-I3VWk2oo.js.map} +1 -1
- package/dist/{ModalTitle-C76ke2eo.js → ModalTitle-CeLM_mX9.js} +2 -2
- package/dist/ModalTitle-CeLM_mX9.js.map +1 -0
- package/dist/{MultiActionButtonGroup-DokHbPgM.js → MultiActionButtonGroup-CYZ8IgSf.js} +2 -2
- package/dist/{MultiActionButtonGroup-DokHbPgM.js.map → MultiActionButtonGroup-CYZ8IgSf.js.map} +1 -1
- package/dist/{PageToolbar-DExqEYGl.js → PageToolbar-BKVJiDAL.js} +2 -2
- package/dist/{PageToolbar-DExqEYGl.js.map → PageToolbar-BKVJiDAL.js.map} +1 -1
- package/dist/{RadioGroup-DXPbSj8P.js → RadioGroup-Byc2mym4.js} +2 -2
- package/dist/{RadioGroup-DXPbSj8P.js.map → RadioGroup-Byc2mym4.js.map} +1 -1
- package/dist/{Search-5uQAiNIP.js → Search-Dhig_hPD.js} +2 -2
- package/dist/{Search-5uQAiNIP.js.map → Search-Dhig_hPD.js.map} +1 -1
- package/dist/{SearchInput-D4UgAXJK.js → SearchInput-92r2bkA7.js} +3 -3
- package/dist/{SearchInput-D4UgAXJK.js.map → SearchInput-92r2bkA7.js.map} +1 -1
- package/dist/{SectionNav-OkKCjSA-.js → SectionNav-CNLJsiVG.js} +4 -4
- package/dist/{SectionNav-OkKCjSA-.js.map → SectionNav-CNLJsiVG.js.map} +1 -1
- package/dist/{Sheet-Chf0kX1E.js → Sheet-OparjIi-.js} +2 -2
- package/dist/{Sheet-Chf0kX1E.js.map → Sheet-OparjIi-.js.map} +1 -1
- package/dist/{Sidebar-D3cY1kha.js → Sidebar-CLEbBtNY.js} +190 -25
- package/dist/Sidebar-CLEbBtNY.js.map +1 -0
- package/dist/{Slider-CRf-bB4y.js → Slider-DLclvRws.js} +2 -2
- package/dist/{Slider-CRf-bB4y.js.map → Slider-DLclvRws.js.map} +1 -1
- package/dist/{TextInput-DH5SkbsE.js → TextInput-CFTf4Cz1.js} +5 -4
- package/dist/TextInput-CFTf4Cz1.js.map +1 -0
- package/dist/{ThemeProvider-2RMdVGxO.js → ThemeProvider-DAmNn9Sd.js} +2 -2
- package/dist/{ThemeProvider-2RMdVGxO.js.map → ThemeProvider-DAmNn9Sd.js.map} +1 -1
- package/dist/{Timeline-BG5Xogxh.js → Timeline-C8-wnRRr.js} +2 -2
- package/dist/{Timeline-BG5Xogxh.js.map → Timeline-C8-wnRRr.js.map} +1 -1
- package/dist/{ZoomableSvg-pooPb1Cu.js → ZoomableSvg-BZlk3PFJ.js} +3 -3
- package/dist/{ZoomableSvg-pooPb1Cu.js.map → ZoomableSvg-BZlk3PFJ.js.map} +1 -1
- package/dist/editors.js +2 -2
- package/dist/flint.js +1 -1
- package/dist/index.d.ts +900 -561
- package/dist/index.js +39 -37
- package/dist/internal/blocks/AIKit.js +2 -2
- package/dist/internal/blocks/BrandEditor.js +1 -1
- package/dist/internal/blocks/ChipEditor.js +137 -0
- package/dist/internal/blocks/ChipEditor.js.map +1 -0
- package/dist/internal/blocks/ClipboardButton.js +1 -1
- package/dist/internal/blocks/ColorInput.js +1 -1
- package/dist/internal/blocks/ColorModeSwitch.js +1 -1
- package/dist/internal/blocks/Command.js +1 -1
- package/dist/internal/blocks/CopyField.js +1 -1
- package/dist/internal/blocks/DateInput.js +1 -1
- package/dist/internal/blocks/DateRangeInput.js +1 -1
- package/dist/internal/blocks/ExportMenu.js +2 -2
- package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +1 -1
- package/dist/internal/blocks/Form.js +1 -1
- package/dist/internal/blocks/FormGroup.js +1 -1
- package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
- package/dist/internal/blocks/MultiCombobox.js +18 -28
- package/dist/internal/blocks/MultiCombobox.js.map +1 -1
- package/dist/internal/blocks/NumberInput.js +1 -1
- package/dist/internal/blocks/PageToolbar.js +1 -1
- package/dist/internal/blocks/PasswordInput.js +2 -2
- package/dist/internal/blocks/SearchInput.js +1 -1
- package/dist/internal/blocks/SearchInputWithNavigation.js +2 -2
- package/dist/internal/blocks/SectionNav.js +1 -1
- package/dist/internal/blocks/SplitButton.js +1 -1
- package/dist/internal/blocks/TextInput.js +1 -1
- package/dist/internal/blocks/Textarea.js +1 -1
- package/dist/internal/blocks/TimeInput.js +2 -2
- package/dist/internal/components/Alert.js +1 -1
- package/dist/internal/components/Avatar.js +1 -1
- package/dist/internal/components/Badge.js +1 -1
- package/dist/internal/components/Breadcrumb.js +1 -1
- package/dist/internal/components/Checkbox.js +1 -1
- package/dist/internal/components/Clipboard.js +1 -1
- package/dist/internal/components/CollapsiblePanel.js +1 -1
- package/dist/internal/components/Combobox.js +1 -1
- package/dist/internal/components/DropdownMenu.js +1 -1
- package/dist/internal/components/FileUpload.js +1 -1
- package/dist/internal/components/InputGroup.js +1 -1
- package/dist/internal/components/Kbd.js +1 -1
- package/dist/internal/components/Modal.js +1 -1
- package/dist/internal/components/RadioGroup.js +1 -1
- package/dist/internal/components/Search.js +1 -1
- package/dist/internal/components/Sheet.js +1 -1
- package/dist/internal/components/Sidebar.js +1 -1
- package/dist/internal/components/Slider.js +1 -1
- package/dist/internal/components/StatusDot.js +67 -0
- package/dist/internal/components/StatusDot.js.map +1 -0
- package/dist/internal/components/ThemeProvider.js +1 -1
- package/dist/internal/components/Timeline.js +1 -1
- package/dist/internal/components/Tooltip.js +2 -2
- package/dist/internal/components/Truncated.js +1 -1
- package/dist/internal/components/ZoomableSvg.js +1 -1
- package/dist/internal/hooks/useScrollbarPosition.js +1 -1
- package/dist/internal/utils/mergeRefs.js +27 -9
- package/dist/internal/utils/mergeRefs.js.map +1 -1
- package/dist/internal/utils.js +2 -2
- package/dist/markdown-editor.js +5 -5
- package/dist/mermaid.js +1 -1
- package/dist/rjsf.js +9 -9
- package/dist/styles.css +1 -1
- package/dist/testing.js +1 -1
- package/dist/useElement-BK3AAiIf.js +24 -0
- package/dist/useElement-BK3AAiIf.js.map +1 -0
- package/dist/{utils-DSvrhU8E.js → utils-BW4AE8VP.js} +33 -7
- package/dist/utils-BW4AE8VP.js.map +1 -0
- package/package.json +14 -13
- package/dist/AIKit-BExvt9pt.js.map +0 -1
- package/dist/DropdownMenu-CASr-jgl.js.map +0 -1
- package/dist/InputGroup-DcZRlVnN.js.map +0 -1
- package/dist/ModalTitle-C76ke2eo.js.map +0 -1
- package/dist/Sidebar-D3cY1kha.js.map +0 -1
- package/dist/TextInput-DH5SkbsE.js.map +0 -1
- package/dist/useElement-DkF6esWL.js +0 -15
- package/dist/useElement-DkF6esWL.js.map +0 -1
- package/dist/utils-DSvrhU8E.js.map +0 -1
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { cn } from "./classNames-BzrkDnOD.js";
|
|
2
|
-
import { Tooltip, TooltipProvider } from "./utils-
|
|
3
|
-
import { AlertCircle, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CirclePause, CircleX, Loader2, Maximize2, Minimize2, Play, Send, Sparkles, ThumbsDown, ThumbsUp, TriangleAlert, X, XCircle } from "./icons-Cek151jC.js";
|
|
2
|
+
import { Tooltip, TooltipProvider } from "./utils-BW4AE8VP.js";
|
|
3
|
+
import { AlertCircle, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Ban, BarChart3, Bell, Bug, Calendar, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Circle, CircleAlert, CircleCheck, CircleHelp, CirclePause, CirclePlay, CirclePlus, CircleStop, CircleX, Clock, Cog, Copy, Database, Download, Ellipsis, ExternalLink, Eye, File, FilePen, FileText, Flag, Folder, Gauge, GitBranch, GitMerge, GitPullRequest, Globe, Hand, History, Hourglass, Info, Key, Layers, LineChart, Link, List, ListChecks, Loader2, Lock, Mail, Maximize2, Minimize2, Minus, Pencil, Play, Plus, RefreshCw, Rocket, RotateCcw, RotateCw, Save, Search, Send, Settings, Sparkles, Star, Table, ThumbsDown, ThumbsUp, Trash2, TriangleAlert, Upload, User, Users, WandSparkles, Warehouse, Workflow, X, XCircle, Zap } from "./icons-Cek151jC.js";
|
|
4
4
|
import { Button } from "./Button-DsRrU5F1.js";
|
|
5
5
|
import { ButtonGroup } from "./internal/components/ButtonGroup.js";
|
|
6
|
-
import { ClipboardButton } from "./ClipboardButton-
|
|
6
|
+
import { ClipboardButton } from "./ClipboardButton-KHQnPEh_.js";
|
|
7
7
|
import { FullScreenModal, FullScreenModalBody, FullScreenModalHeader } from "./FullScreenModal-CN9nAs72.js";
|
|
8
8
|
import { Truncated } from "./internal/components/Truncated.js";
|
|
9
9
|
import { HoverCard } from "./HoverCard-B9ZMJxjn.js";
|
|
10
|
-
import { Command } from "./Command-
|
|
11
|
-
import { TextInput } from "./TextInput-
|
|
12
|
-
import { Checkbox } from "./Checkbox-
|
|
13
|
-
import { Badge } from "./Badge-
|
|
10
|
+
import { Command } from "./Command-BDU3C1gg.js";
|
|
11
|
+
import { TextInput } from "./TextInput-CFTf4Cz1.js";
|
|
12
|
+
import { Checkbox } from "./Checkbox-Dq5fPNrO.js";
|
|
13
|
+
import { Badge } from "./Badge-o5jURtpB.js";
|
|
14
14
|
import { Textarea } from "./internal/blocks/Textarea.js";
|
|
15
|
-
import { Table } from "./Table-CvYuTuNU.js";
|
|
15
|
+
import { Table as Table$1 } from "./Table-CvYuTuNU.js";
|
|
16
16
|
import { DiagramSkeleton } from "./internal/blocks/DiagramShared/DiagramSkeleton.js";
|
|
17
17
|
import { DiagramSource } from "./DiagramSource-C5_d8Gdj.js";
|
|
18
18
|
import { Progress } from "./internal/components/Progress.js";
|
|
19
19
|
import { Slot } from "@radix-ui/react-slot";
|
|
20
|
-
import { isValidElement, useCallback, useEffect, useId, useRef, useState } from "react";
|
|
20
|
+
import { createContext, isValidElement, use, useCallback, useEffect, useId, useRef, useState } from "react";
|
|
21
21
|
import { cva } from "class-variance-authority";
|
|
22
22
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
23
23
|
import TextareaAutosize from "react-textarea-autosize";
|
|
@@ -26,13 +26,26 @@ import remarkGfm from "remark-gfm";
|
|
|
26
26
|
//#region src/blocks/AIKit/AnswerActions.tsx
|
|
27
27
|
const VOTE_BUTTONS = [{
|
|
28
28
|
vote: "up",
|
|
29
|
-
label: "Good answer",
|
|
30
29
|
icon: /* @__PURE__ */ jsx(ThumbsUp, { className: "tw:size-4" })
|
|
31
30
|
}, {
|
|
32
31
|
vote: "down",
|
|
33
|
-
label: "Bad answer",
|
|
34
32
|
icon: /* @__PURE__ */ jsx(ThumbsDown, { className: "tw:size-4" })
|
|
35
33
|
}];
|
|
34
|
+
const DEFAULT_LABELS = {
|
|
35
|
+
up: "Good answer",
|
|
36
|
+
down: "Bad answer"
|
|
37
|
+
};
|
|
38
|
+
/** Roomier for a panel a reader sits in; the compact one suits a row inside a dense transcript. */
|
|
39
|
+
const SIZES = {
|
|
40
|
+
small: {
|
|
41
|
+
button: "icon-xs",
|
|
42
|
+
gap: "extra-small"
|
|
43
|
+
},
|
|
44
|
+
medium: {
|
|
45
|
+
button: "icon-sm",
|
|
46
|
+
gap: "little-small"
|
|
47
|
+
}
|
|
48
|
+
};
|
|
36
49
|
/**
|
|
37
50
|
* What a reader can do with a finished answer: take it away, and say whether it was any good.
|
|
38
51
|
*
|
|
@@ -45,31 +58,58 @@ const VOTE_BUTTONS = [{
|
|
|
45
58
|
* - The recorded vote and the write are the caller's: the two chats vote against different
|
|
46
59
|
* endpoints, and only the caller knows whether a turn is finished enough to rate.
|
|
47
60
|
*/
|
|
48
|
-
const AnswerActions = ({ copyText, vote, onVote }) => {
|
|
61
|
+
const AnswerActions = ({ copyText, vote, onVote, onCopy, actions, labels = DEFAULT_LABELS, confirmation, size = "small", layout = "inline" }) => {
|
|
62
|
+
const { button, gap } = SIZES[size];
|
|
63
|
+
const votes = VOTE_BUTTONS.map(({ vote: candidate, icon }) => {
|
|
64
|
+
const isRecorded = vote === candidate;
|
|
65
|
+
const label = labels[candidate];
|
|
66
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
67
|
+
tooltip: label,
|
|
68
|
+
placement: "top",
|
|
69
|
+
triggerAsChild: true,
|
|
70
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
71
|
+
size: button,
|
|
72
|
+
variant: "invisible",
|
|
73
|
+
className: cn(isRecorded && candidate === "up" && "tw:bg-success/10 tw:text-success tw:hover:not-disabled:bg-success/20 tw:hover:not-disabled:text-success", isRecorded && candidate === "down" && "tw:bg-destructive/10 tw:text-destructive tw:hover:not-disabled:bg-destructive/20 tw:hover:not-disabled:text-destructive"),
|
|
74
|
+
"aria-label": label,
|
|
75
|
+
"aria-pressed": isRecorded,
|
|
76
|
+
onClick: () => onVote(candidate),
|
|
77
|
+
children: icon
|
|
78
|
+
})
|
|
79
|
+
}, candidate);
|
|
80
|
+
});
|
|
81
|
+
const copy = copyText ? /* @__PURE__ */ jsx(ClipboardButton, {
|
|
82
|
+
text: copyText,
|
|
83
|
+
tooltip: "Copy answer",
|
|
84
|
+
size: button,
|
|
85
|
+
variant: "invisible",
|
|
86
|
+
onClick: onCopy
|
|
87
|
+
}) : null;
|
|
88
|
+
const said = confirmation ? /* @__PURE__ */ jsx("span", {
|
|
89
|
+
"aria-live": "polite",
|
|
90
|
+
className: "tw:ml-1 tw:text-xs tw:font-medium tw:text-foreground tw:empty:hidden tw:animate-in tw:fade-in tw:slide-in-from-left-1 tw:duration-300",
|
|
91
|
+
children: vote ? confirmation[vote] : ""
|
|
92
|
+
}) : null;
|
|
93
|
+
if (layout === "split") return /* @__PURE__ */ jsxs("div", {
|
|
94
|
+
className: "tw:flex tw:w-full tw:items-center tw:justify-between tw:gap-2",
|
|
95
|
+
children: [/* @__PURE__ */ jsxs(ButtonGroup, {
|
|
96
|
+
space: gap,
|
|
97
|
+
"aria-label": "Answer actions",
|
|
98
|
+
children: [votes, said]
|
|
99
|
+
}), /* @__PURE__ */ jsxs(ButtonGroup, {
|
|
100
|
+
space: gap,
|
|
101
|
+
children: [actions, copy]
|
|
102
|
+
})]
|
|
103
|
+
});
|
|
49
104
|
return /* @__PURE__ */ jsxs(ButtonGroup, {
|
|
50
|
-
space:
|
|
105
|
+
space: gap,
|
|
51
106
|
"aria-label": "Answer actions",
|
|
52
|
-
children: [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const isRecorded = vote === candidate;
|
|
59
|
-
return /* @__PURE__ */ jsx(Tooltip, {
|
|
60
|
-
tooltip: label,
|
|
61
|
-
placement: "top",
|
|
62
|
-
triggerAsChild: true,
|
|
63
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
64
|
-
size: "icon-xs",
|
|
65
|
-
variant: isRecorded ? "secondary" : "invisible",
|
|
66
|
-
"aria-label": label,
|
|
67
|
-
"aria-pressed": isRecorded,
|
|
68
|
-
onClick: () => onVote(candidate),
|
|
69
|
-
children: icon
|
|
70
|
-
})
|
|
71
|
-
}, candidate);
|
|
72
|
-
})]
|
|
107
|
+
children: [
|
|
108
|
+
copy,
|
|
109
|
+
votes,
|
|
110
|
+
actions,
|
|
111
|
+
said
|
|
112
|
+
]
|
|
73
113
|
});
|
|
74
114
|
};
|
|
75
115
|
//#endregion
|
|
@@ -98,6 +138,12 @@ const variantClasses = {
|
|
|
98
138
|
dismissed: "tw:text-muted-foreground tw:focus-visible:outline-ring tw:not-disabled:hover:text-foreground"
|
|
99
139
|
};
|
|
100
140
|
const iconClasses = "tw:size-3.5 tw:shrink-0 tw:basis-3.5 tw:opacity-70";
|
|
141
|
+
const variantStatus = {
|
|
142
|
+
success: "finished",
|
|
143
|
+
error: "failed",
|
|
144
|
+
processing: "running",
|
|
145
|
+
dismissed: "not run"
|
|
146
|
+
};
|
|
101
147
|
const renderVariantIcon$1 = (variant) => {
|
|
102
148
|
if (variant === "processing") return /* @__PURE__ */ jsx(Loader2, { className: cn(iconClasses, "tw:animate-spin") });
|
|
103
149
|
if (variant === "error" || variant === "dismissed") return /* @__PURE__ */ jsx(XCircle, { className: iconClasses });
|
|
@@ -108,13 +154,21 @@ const CollapsibleStepHeader = ({ isExpanded, onToggle, label, variant = "success
|
|
|
108
154
|
type: "button",
|
|
109
155
|
"aria-expanded": isExpanded,
|
|
110
156
|
onClick: onToggle,
|
|
157
|
+
"data-variant": variant,
|
|
111
158
|
className: cn("tw:flex tw:w-fit tw:items-center tw:gap-1.5 tw:text-left tw:text-xs tw:font-medium tw:transition-colors tw:focus-visible:outline tw:focus-visible:outline-2 tw:focus-visible:outline-offset-2 tw:disabled:opacity-40", variantClasses[variant]),
|
|
112
159
|
children: [renderVariantIcon$1(variant), /* @__PURE__ */ jsxs("span", {
|
|
113
160
|
className: "tw:min-w-0",
|
|
114
|
-
children: [
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
161
|
+
children: [
|
|
162
|
+
label,
|
|
163
|
+
/* @__PURE__ */ jsx("span", {
|
|
164
|
+
className: "tw:sr-only",
|
|
165
|
+
children: ` (${variantStatus[variant]})`
|
|
166
|
+
}),
|
|
167
|
+
/* @__PURE__ */ jsxs("span", {
|
|
168
|
+
className: "tw:whitespace-nowrap",
|
|
169
|
+
children: ["", /* @__PURE__ */ jsx(ChevronDown, { className: cn("tw:ml-1.5 tw:inline-block tw:size-3.5 tw:align-middle tw:transition-transform", isExpanded && "tw:rotate-180") })]
|
|
170
|
+
})
|
|
171
|
+
]
|
|
118
172
|
})]
|
|
119
173
|
});
|
|
120
174
|
};
|
|
@@ -189,6 +243,250 @@ const AnswerSummary = ({ items, variant = "success", noAnswerLabel = "No answer"
|
|
|
189
243
|
});
|
|
190
244
|
};
|
|
191
245
|
//#endregion
|
|
246
|
+
//#region src/blocks/AIKit/ChatInlineIcon.tsx
|
|
247
|
+
/**
|
|
248
|
+
* The Lucide component each `icon:<name>` token draws.
|
|
249
|
+
*
|
|
250
|
+
* - The names are FontAwesome-solid ones: kai-agent's prompt teaches that vocabulary, and
|
|
251
|
+
* `@keboola/agent-vocabulary` publishes it as `CHAT_INLINE_ICON_NAMES`.
|
|
252
|
+
* - `satisfies Record<ChatInlineIconName, LucideIcon>` is the drift guard — a name the prompt
|
|
253
|
+
* teaches and this map lacks is a compile error, not a token that silently disappears from an
|
|
254
|
+
* answer.
|
|
255
|
+
* - Several names map to one component on purpose: FA distinguishes `gear` from `cog` and
|
|
256
|
+
* `warning` from `triangle-exclamation`; Lucide draws each pair with one icon.
|
|
257
|
+
* - Read through `Object.hasOwn`, never by plain index: a model-emitted `icon:constructor`
|
|
258
|
+
* otherwise answers with an `Object.prototype` member and React tries to render it.
|
|
259
|
+
*/
|
|
260
|
+
const ICON_REGISTRY = {
|
|
261
|
+
"alert-circle": AlertCircle,
|
|
262
|
+
"arrow-down": ArrowDown,
|
|
263
|
+
"arrow-left": ArrowLeft,
|
|
264
|
+
"arrow-right": ArrowRight,
|
|
265
|
+
"arrow-rotate-back": RotateCcw,
|
|
266
|
+
"arrow-rotate-left": RotateCcw,
|
|
267
|
+
"arrow-rotate-right": RotateCw,
|
|
268
|
+
"arrow-up": ArrowUp,
|
|
269
|
+
"arrow-up-right-from-square": ExternalLink,
|
|
270
|
+
"arrows-rotate": RefreshCw,
|
|
271
|
+
analytics: BarChart3,
|
|
272
|
+
ban: Ban,
|
|
273
|
+
bell: Bell,
|
|
274
|
+
bug: Bug,
|
|
275
|
+
calendar: Calendar,
|
|
276
|
+
changes: FilePen,
|
|
277
|
+
"chart-line": LineChart,
|
|
278
|
+
check: Check,
|
|
279
|
+
"check-circle": CircleCheck,
|
|
280
|
+
"chevron-down": ChevronDown,
|
|
281
|
+
"chevron-left": ChevronLeft,
|
|
282
|
+
"chevron-right": ChevronRight,
|
|
283
|
+
"chevron-up": ChevronUp,
|
|
284
|
+
circle: Circle,
|
|
285
|
+
"circle-check": CircleCheck,
|
|
286
|
+
"circle-exclamation": CircleAlert,
|
|
287
|
+
"circle-info": Info,
|
|
288
|
+
"circle-pause": CirclePause,
|
|
289
|
+
"circle-play": CirclePlay,
|
|
290
|
+
"circle-plus": CirclePlus,
|
|
291
|
+
"circle-question": CircleHelp,
|
|
292
|
+
"circle-stop": CircleStop,
|
|
293
|
+
"circle-xmark": CircleX,
|
|
294
|
+
clock: Clock,
|
|
295
|
+
"clock-rotate-left": History,
|
|
296
|
+
"code-branch": GitBranch,
|
|
297
|
+
"code-merge": GitMerge,
|
|
298
|
+
"code-pull-request": GitPullRequest,
|
|
299
|
+
cog: Cog,
|
|
300
|
+
configuration: Settings,
|
|
301
|
+
copy: Copy,
|
|
302
|
+
database: Database,
|
|
303
|
+
"diagram-project": Workflow,
|
|
304
|
+
download: Download,
|
|
305
|
+
ellipsis: Ellipsis,
|
|
306
|
+
envelope: Mail,
|
|
307
|
+
"exclamation-circle": CircleAlert,
|
|
308
|
+
"exclamation-triangle": TriangleAlert,
|
|
309
|
+
eye: Eye,
|
|
310
|
+
file: File,
|
|
311
|
+
"file-lines": FileText,
|
|
312
|
+
flag: Flag,
|
|
313
|
+
folder: Folder,
|
|
314
|
+
gauge: Gauge,
|
|
315
|
+
gear: Settings,
|
|
316
|
+
globe: Globe,
|
|
317
|
+
hand: Hand,
|
|
318
|
+
"hourglass-half": Hourglass,
|
|
319
|
+
"info-circle": Info,
|
|
320
|
+
key: Key,
|
|
321
|
+
"layer-group": Layers,
|
|
322
|
+
link: Link,
|
|
323
|
+
"list-check": ListChecks,
|
|
324
|
+
"list-ul": List,
|
|
325
|
+
lock: Lock,
|
|
326
|
+
"magnifying-glass": Search,
|
|
327
|
+
minus: Minus,
|
|
328
|
+
"paper-plane-top": Send,
|
|
329
|
+
pen: Pencil,
|
|
330
|
+
play: Play,
|
|
331
|
+
plus: Plus,
|
|
332
|
+
refresh: RefreshCw,
|
|
333
|
+
rocket: Rocket,
|
|
334
|
+
save: Save,
|
|
335
|
+
"save-changes": Save,
|
|
336
|
+
search: Search,
|
|
337
|
+
spinner: Loader2,
|
|
338
|
+
sparkles: Sparkles,
|
|
339
|
+
star: Star,
|
|
340
|
+
stop: CircleStop,
|
|
341
|
+
table: Table,
|
|
342
|
+
"thumbs-down": ThumbsDown,
|
|
343
|
+
"thumbs-up": ThumbsUp,
|
|
344
|
+
transformation: WandSparkles,
|
|
345
|
+
trash: Trash2,
|
|
346
|
+
"triangle-exclamation": TriangleAlert,
|
|
347
|
+
upload: Upload,
|
|
348
|
+
user: User,
|
|
349
|
+
"user-group": Users,
|
|
350
|
+
warning: TriangleAlert,
|
|
351
|
+
warehouse: Warehouse,
|
|
352
|
+
xmark: X,
|
|
353
|
+
zap: Zap
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* Extra classes a single name needs on top of the shared list.
|
|
357
|
+
*
|
|
358
|
+
* - `spinner` is the only one: `MAINTAINING.md` maps it to `Loader2` *plus* `tw:animate-spin`,
|
|
359
|
+
* because Lucide draws the arc and leaves the motion to the caller. A still `Loader2` reads as
|
|
360
|
+
* a damaged ring, not as waiting.
|
|
361
|
+
* - Read through `Object.hasOwn`, for the same reason the registry is.
|
|
362
|
+
*/
|
|
363
|
+
const ICON_CLASSES = { spinner: "tw:animate-spin" };
|
|
364
|
+
/**
|
|
365
|
+
* A whole `icon:<name>` token and nothing else.
|
|
366
|
+
*
|
|
367
|
+
* - Anchored at both ends, and over the same name shape `remarkIconSyntax` (in
|
|
368
|
+
* `@keboola/kai-chat`) matches when it turns bare text into an `inlineCode` node. The two
|
|
369
|
+
* have to agree: a looser reader here accepts inline code the plugin never produced.
|
|
370
|
+
* - That is what keeps ordinary inline code ordinary. A prefix test alone claims
|
|
371
|
+
* `` `icon: database` ``, `` `icon:` `` and a fence body starting `icon:` — text the plugin
|
|
372
|
+
* never turned into a token, and none of it is an icon the author asked for.
|
|
373
|
+
*/
|
|
374
|
+
const TOKEN = /^icon:([a-z][a-z0-9-]*)$/;
|
|
375
|
+
/** Built once: an answer is re-parsed per streamed chunk, so `isArriving` runs per code node. */
|
|
376
|
+
const ICON_NAMES = Object.keys(ICON_REGISTRY);
|
|
377
|
+
/**
|
|
378
|
+
* Whether a name is only ever the start of one this registry draws.
|
|
379
|
+
*
|
|
380
|
+
* - Strictly shorter, so a whole name is never read as an unfinished one.
|
|
381
|
+
* - `tabl` → `true` (`table`); `unicorn` and `table-name` → `false`.
|
|
382
|
+
*/
|
|
383
|
+
const isArriving = (name) => ICON_NAMES.some((key) => key.length > name.length && key.startsWith(name));
|
|
384
|
+
/**
|
|
385
|
+
* The icon name in a code node's text, if the text is a token — drawn, or still arriving.
|
|
386
|
+
*
|
|
387
|
+
* - `icon:table` → `table`; anything else, including `icon: table`, `icon:Table` and
|
|
388
|
+
* `icon:unicorn` → `undefined`.
|
|
389
|
+
* - A name the registry lacks answers `undefined` on purpose, so every call site's own `<code>`
|
|
390
|
+
* fall-through keeps the text its author typed. `icon:` is an ordinary way to name a property,
|
|
391
|
+
* and this renderer draws vendor descriptions and dashboard text as well as agent answers.
|
|
392
|
+
* - A name that only starts one it draws is a token still arriving, answered with so that
|
|
393
|
+
* `<ChatInlineIcon />` draws nothing: an answer streams `icon:t`, `icon:ta`, `icon:tab` before
|
|
394
|
+
* the name lands, and each of those as code is a chip that reflows the line for a frame.
|
|
395
|
+
* - A name that is both — `save`, the start of `save-changes` — is drawn now; waiting would leave
|
|
396
|
+
* a finished token blank for the rest of the answer.
|
|
397
|
+
* - The token reaches a renderer as inline code either because the model wrote it in backticks
|
|
398
|
+
* or because a remark plugin turned the bare text into an `inlineCode` node. Parsing the
|
|
399
|
+
* token is the vocabulary's; producing it is the app's, since the plugin is workspace-internal.
|
|
400
|
+
*/
|
|
401
|
+
const getChatInlineIconName = (text) => {
|
|
402
|
+
const name = TOKEN.exec(text)?.[1];
|
|
403
|
+
if (name === void 0) return void 0;
|
|
404
|
+
return Object.hasOwn(ICON_REGISTRY, name) || isArriving(name) ? name : void 0;
|
|
405
|
+
};
|
|
406
|
+
/**
|
|
407
|
+
* One inline `icon:<name>` token, drawn at the size of the text it leads.
|
|
408
|
+
*
|
|
409
|
+
* - An unknown name renders nothing, which is how a token still arriving (`icon:tabl`) stays
|
|
410
|
+
* blank instead of flashing as a code chip. A name `getChatInlineIconName` withholds keeps its
|
|
411
|
+
* text at the call site's own `<code>` fall-through and never reaches here.
|
|
412
|
+
* - `size-[1em]` tracks the font size, so the icon matches a heading instead of sitting small
|
|
413
|
+
* beside it, and `align-[-0.125em]` on an inline-block optically centres it against the
|
|
414
|
+
* baseline.
|
|
415
|
+
* - `mr` tops up the single source space, which is a word gap and reads as cramped between a
|
|
416
|
+
* glyph and the phrase it labels.
|
|
417
|
+
* - A heavier stroke so an outline icon holds its own next to the bold weight a heading carries.
|
|
418
|
+
* - `spinner` also spins; the mapping owns that, not the caller. See `ICON_CLASSES`.
|
|
419
|
+
* - `aria-hidden`, because the glyph is decorative: the token's text is removed from the answer and
|
|
420
|
+
* nothing replaces it as a label, so a reader that announces unnamed inline SVGs would read an
|
|
421
|
+
* unlabelled graphic before every phrase the agent decorates.
|
|
422
|
+
*/
|
|
423
|
+
const ChatInlineIcon = ({ name }) => {
|
|
424
|
+
const Icon = Object.hasOwn(ICON_REGISTRY, name) ? ICON_REGISTRY[name] : void 0;
|
|
425
|
+
if (Icon === void 0) return null;
|
|
426
|
+
return /* @__PURE__ */ jsx(Icon, {
|
|
427
|
+
"aria-hidden": true,
|
|
428
|
+
className: cn("tw:mr-[0.15em] tw:inline-block tw:size-[1em] tw:shrink-0 tw:align-[-0.125em] tw:text-muted-foreground", Object.hasOwn(ICON_CLASSES, name) ? ICON_CLASSES[name] : void 0),
|
|
429
|
+
strokeWidth: 2.25
|
|
430
|
+
});
|
|
431
|
+
};
|
|
432
|
+
//#endregion
|
|
433
|
+
//#region src/blocks/AIKit/InlineStepGroup.tsx
|
|
434
|
+
/**
|
|
435
|
+
* The surface colour, as the custom property the step icons mask against.
|
|
436
|
+
*
|
|
437
|
+
* - Annotated rather than asserted: `CSSProperties` admits no custom property of its own, and
|
|
438
|
+
* an intersection with the one this sets keeps the value type-checked at the assignment.
|
|
439
|
+
*/
|
|
440
|
+
const surfaceStyle = (surface) => ({ "--inline-step-surface": surface });
|
|
441
|
+
/**
|
|
442
|
+
* A run of steps an agent took, behind one line that says how far along it is.
|
|
443
|
+
*
|
|
444
|
+
* - The frame only. What a step *says* is the host's — one product names buckets and tables,
|
|
445
|
+
* another must never put those words on screen — so the rows arrive as children and this
|
|
446
|
+
* supplies the header, the collapse, the connector and the rhythm they share.
|
|
447
|
+
* - One rule behind the whole run at the icon centre line: the list's `pl-5` plus half a
|
|
448
|
+
* `size-5` icon box. Drawn once rather than per row, so it does not vanish beside a row that
|
|
449
|
+
* happens to have no icon.
|
|
450
|
+
* - `grid-template-rows` rather than height, because the rows are text of unknown length and a
|
|
451
|
+
* transition needs a number at both ends. The children stay mounted while closed: a group the
|
|
452
|
+
* reader reopens must not re-run whatever mounting a row costs. A closed group is `inert` and
|
|
453
|
+
* `aria-hidden`, so staying mounted does not mean staying reachable.
|
|
454
|
+
* - Expansion is the caller's state. Whether a group opens itself while work is happening, and
|
|
455
|
+
* whether a reader's own toggle outranks that, is a transcript-level decision.
|
|
456
|
+
* - `role="group"` rather than the `<section>`'s own landmark semantics: a transcript draws one
|
|
457
|
+
* of these per run of steps, so a long conversation would hand a screen reader a landmark
|
|
458
|
+
* list of dozens of identically named `region`s and drown the page's real landmarks in it.
|
|
459
|
+
* The name still reads — a named `group` is announced by it — and it is the role the
|
|
460
|
+
* grounding panel already gives its repeated named answer sections.
|
|
461
|
+
*/
|
|
462
|
+
const InlineStepGroup = ({ label, variant = "success", isExpanded, onToggle, surface = "rgb(var(--background))", "aria-label": ariaLabel, "data-testid": testId, className, children }) => /* @__PURE__ */ jsxs("section", {
|
|
463
|
+
role: "group",
|
|
464
|
+
className: cn("tw:flex tw:flex-col", className),
|
|
465
|
+
"aria-label": ariaLabel,
|
|
466
|
+
"data-testid": testId,
|
|
467
|
+
children: [/* @__PURE__ */ jsx(CollapsibleStepHeader, {
|
|
468
|
+
isExpanded,
|
|
469
|
+
onToggle,
|
|
470
|
+
label,
|
|
471
|
+
variant
|
|
472
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
473
|
+
className: cn("tw:grid tw:transition-[grid-template-rows] tw:duration-200 tw:ease-out tw:motion-reduce:transition-none", isExpanded ? "tw:grid-rows-[1fr]" : "tw:grid-rows-[0fr]"),
|
|
474
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
475
|
+
className: "tw:min-h-0 tw:overflow-hidden",
|
|
476
|
+
inert: !isExpanded,
|
|
477
|
+
"aria-hidden": isExpanded ? void 0 : true,
|
|
478
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
479
|
+
style: surfaceStyle(surface),
|
|
480
|
+
className: "tw:relative tw:flex tw:flex-col tw:gap-4 tw:pt-4 tw:pl-5",
|
|
481
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
482
|
+
"aria-hidden": "true",
|
|
483
|
+
className: "tw:pointer-events-none tw:absolute tw:top-4 tw:bottom-0 tw:left-[30px] tw:w-px tw:bg-border"
|
|
484
|
+
}), children]
|
|
485
|
+
})
|
|
486
|
+
})
|
|
487
|
+
})]
|
|
488
|
+
});
|
|
489
|
+
//#endregion
|
|
192
490
|
//#region src/blocks/AIKit/DiagramBlock.tsx
|
|
193
491
|
/**
|
|
194
492
|
* One diagram block: a skeleton while its chunk loads, the fence source plus a retry if it fails.
|
|
@@ -318,6 +616,41 @@ const DIAGRAM_CHUNKS = {
|
|
|
318
616
|
* every other app in the same bundle holds a fence back.
|
|
319
617
|
*/
|
|
320
618
|
const DIAGRAM_LANGUAGES = Object.freeze(Object.keys(DIAGRAM_CHUNKS));
|
|
619
|
+
/**
|
|
620
|
+
* Whether the `code` being rendered sits inside a fenced block.
|
|
621
|
+
*
|
|
622
|
+
* - A fence with no info string reaches `code` with no `language-…` class, exactly like inline
|
|
623
|
+
* code, so the class cannot tell the two apart — `pre` is the only boundary that can.
|
|
624
|
+
* - Set by the `pre` renderer, read by `code`: React renders `pre` before its children, so the
|
|
625
|
+
* value is in place by the time the `code` inside it renders.
|
|
626
|
+
*/
|
|
627
|
+
const CodeBlockContext = createContext(false);
|
|
628
|
+
/**
|
|
629
|
+
* The `code` renderer: an `icon:<name>` token becomes the icon it names, anything else stays code.
|
|
630
|
+
*
|
|
631
|
+
* - Only inline code is a candidate: a fenced block's body is code the reader asked to see, and
|
|
632
|
+
* swapping it for a glyph would drop it. `CodeBlockContext` is what separates the two — a fence
|
|
633
|
+
* with no info string carries no `language-…` class either, so the class cannot.
|
|
634
|
+
* - The token reaches here as inline code because `remarkIconSyntax` (in `@keboola/kai-chat`)
|
|
635
|
+
* turned the bare text into one, or because the model wrote the backticks itself. Producing the
|
|
636
|
+
* token is the app's, drawing it is this package's.
|
|
637
|
+
* - A name no icon answers to stays code: `getChatInlineIconName` answers only for names the
|
|
638
|
+
* registry can draw or is still receiving, and this renderer draws vendor descriptions and
|
|
639
|
+
* dashboard text too, where `icon:` is an ordinary way to name a property.
|
|
640
|
+
* - A name still arriving draws nothing, so a streamed `icon:tabl` is blank for a frame rather
|
|
641
|
+
* than a code chip that reflows the line.
|
|
642
|
+
* - A component rather than an inline renderer so it can read the context.
|
|
643
|
+
*/
|
|
644
|
+
const MarkdownCode = ({ children, className, ...props }) => {
|
|
645
|
+
const { node: _node, ...rest } = props;
|
|
646
|
+
const name = use(CodeBlockContext) ? void 0 : getChatInlineIconName(getText(children));
|
|
647
|
+
if (name !== void 0) return /* @__PURE__ */ jsx(ChatInlineIcon, { name });
|
|
648
|
+
return /* @__PURE__ */ jsx("code", {
|
|
649
|
+
className,
|
|
650
|
+
...rest,
|
|
651
|
+
children
|
|
652
|
+
});
|
|
653
|
+
};
|
|
321
654
|
const baseComponents = {
|
|
322
655
|
pre({ children, ...props }) {
|
|
323
656
|
const child = Array.isArray(children) ? children[0] : children;
|
|
@@ -344,10 +677,14 @@ const baseComponents = {
|
|
|
344
677
|
})
|
|
345
678
|
}), /* @__PURE__ */ jsx("pre", {
|
|
346
679
|
...rest,
|
|
347
|
-
children
|
|
680
|
+
children: /* @__PURE__ */ jsx(CodeBlockContext, {
|
|
681
|
+
value: true,
|
|
682
|
+
children
|
|
683
|
+
})
|
|
348
684
|
})]
|
|
349
685
|
});
|
|
350
686
|
},
|
|
687
|
+
code: MarkdownCode,
|
|
351
688
|
a({ children, href, ...props }) {
|
|
352
689
|
const { node: _node, ...rest } = props;
|
|
353
690
|
const isExternal = !!href && /^https?:\/\//.test(href);
|
|
@@ -365,7 +702,7 @@ const baseComponents = {
|
|
|
365
702
|
const { node: _node, ...rest } = props;
|
|
366
703
|
return /* @__PURE__ */ jsx("div", {
|
|
367
704
|
className: "tw:not-prose tw:my-4",
|
|
368
|
-
children: /* @__PURE__ */ jsx(Table, {
|
|
705
|
+
children: /* @__PURE__ */ jsx(Table$1, {
|
|
369
706
|
bordered: true,
|
|
370
707
|
className: cn("tw:!mb-0", className),
|
|
371
708
|
...rest
|
|
@@ -374,23 +711,23 @@ const baseComponents = {
|
|
|
374
711
|
},
|
|
375
712
|
thead(props) {
|
|
376
713
|
const { node: _node, ...rest } = props;
|
|
377
|
-
return /* @__PURE__ */ jsx(Table.Header, { ...rest });
|
|
714
|
+
return /* @__PURE__ */ jsx(Table$1.Header, { ...rest });
|
|
378
715
|
},
|
|
379
716
|
tbody(props) {
|
|
380
717
|
const { node: _node, ...rest } = props;
|
|
381
|
-
return /* @__PURE__ */ jsx(Table.Body, { ...rest });
|
|
718
|
+
return /* @__PURE__ */ jsx(Table$1.Body, { ...rest });
|
|
382
719
|
},
|
|
383
720
|
tr(props) {
|
|
384
721
|
const { node: _node, ...rest } = props;
|
|
385
|
-
return /* @__PURE__ */ jsx(Table.Row, { ...rest });
|
|
722
|
+
return /* @__PURE__ */ jsx(Table$1.Row, { ...rest });
|
|
386
723
|
},
|
|
387
724
|
th(props) {
|
|
388
725
|
const { node: _node, ...rest } = props;
|
|
389
|
-
return /* @__PURE__ */ jsx(Table.Head, { ...rest });
|
|
726
|
+
return /* @__PURE__ */ jsx(Table$1.Head, { ...rest });
|
|
390
727
|
},
|
|
391
728
|
td(props) {
|
|
392
729
|
const { node: _node, ...rest } = props;
|
|
393
|
-
return /* @__PURE__ */ jsx(Table.Cell, { ...rest });
|
|
730
|
+
return /* @__PURE__ */ jsx(Table$1.Cell, { ...rest });
|
|
394
731
|
}
|
|
395
732
|
};
|
|
396
733
|
/**
|
|
@@ -910,8 +1247,45 @@ const PromptInputAction = ({ ref, onMouseDown, ...rest }) => {
|
|
|
910
1247
|
};
|
|
911
1248
|
//#endregion
|
|
912
1249
|
//#region src/blocks/AIKit/PromptInputCommands.tsx
|
|
913
|
-
|
|
1250
|
+
/** What the menu needs above or below the composer before it will open on that side. */
|
|
1251
|
+
const MENU_HEIGHT = 440;
|
|
1252
|
+
const PromptInputCommands = ({ items, open, onSelect, filterValue, emptyText = "No commands found", onClose, children }) => {
|
|
914
1253
|
const filtered = filterValue ? items.filter((item) => item.label.toLowerCase().includes(filterValue.toLowerCase()) || item.value.toLowerCase().includes(filterValue.toLowerCase())) : items;
|
|
1254
|
+
const [highlighted, setHighlighted] = useState();
|
|
1255
|
+
/**
|
|
1256
|
+
* Which side the menu opens on.
|
|
1257
|
+
*
|
|
1258
|
+
* - Above by default: a composer usually sits at the foot of its panel.
|
|
1259
|
+
* - Below when there is not room above and there is room below — the dashboard composer sits
|
|
1260
|
+
* near the top of the page, where opening upward clipped the list against the header.
|
|
1261
|
+
* - Re-measured while it is open: the composer grows as the message does, and the transcript
|
|
1262
|
+
* above it moves, so the side that fit on open may not fit a moment later.
|
|
1263
|
+
*/
|
|
1264
|
+
const rootRef = useRef(null);
|
|
1265
|
+
const [below, setBelow] = useState(false);
|
|
1266
|
+
const measure = useCallback(() => {
|
|
1267
|
+
const box = rootRef.current?.getBoundingClientRect();
|
|
1268
|
+
if (!box) return;
|
|
1269
|
+
setBelow(box.top < MENU_HEIGHT && window.innerHeight - box.bottom > MENU_HEIGHT);
|
|
1270
|
+
}, []);
|
|
1271
|
+
const attachRoot = useCallback((node) => {
|
|
1272
|
+
rootRef.current = node;
|
|
1273
|
+
if (node) measure();
|
|
1274
|
+
}, [measure]);
|
|
1275
|
+
useEffect(() => {
|
|
1276
|
+
if (!open) return;
|
|
1277
|
+
const node = rootRef.current;
|
|
1278
|
+
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
|
|
1279
|
+
if (node) observer?.observe(node);
|
|
1280
|
+
window.addEventListener("resize", measure);
|
|
1281
|
+
window.addEventListener("scroll", measure, true);
|
|
1282
|
+
return () => {
|
|
1283
|
+
observer?.disconnect();
|
|
1284
|
+
window.removeEventListener("resize", measure);
|
|
1285
|
+
window.removeEventListener("scroll", measure, true);
|
|
1286
|
+
};
|
|
1287
|
+
}, [open, measure]);
|
|
1288
|
+
const highlightedValue = filtered.some((item) => item.value === highlighted) ? highlighted : filtered[0]?.value;
|
|
915
1289
|
const groups = filtered.reduce((acc, item) => {
|
|
916
1290
|
const group = acc[item.group] ?? [];
|
|
917
1291
|
group.push(item);
|
|
@@ -919,22 +1293,36 @@ const PromptInputCommands = ({ items, open, onSelect, filterValue, children }) =
|
|
|
919
1293
|
return acc;
|
|
920
1294
|
}, {});
|
|
921
1295
|
return /* @__PURE__ */ jsxs(Command, {
|
|
1296
|
+
ref: attachRoot,
|
|
922
1297
|
loop: true,
|
|
923
1298
|
className: "tw:relative",
|
|
924
1299
|
shouldFilter: false,
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1300
|
+
value: highlightedValue ?? "",
|
|
1301
|
+
onValueChange: setHighlighted,
|
|
1302
|
+
children: [open && /* @__PURE__ */ jsxs("div", {
|
|
1303
|
+
className: cn("tw:absolute tw:left-0 tw:z-50 tw:w-full tw:max-w-lg", below ? "tw:top-full tw:mt-1" : "tw:bottom-full tw:mb-1", "tw:rounded-[14px] tw:border tw:border-border tw:bg-popover tw:text-popover-foreground tw:shadow-lg", "tw:p-1"),
|
|
1304
|
+
children: [onClose && /* @__PURE__ */ jsx(Button, {
|
|
1305
|
+
type: "button",
|
|
1306
|
+
size: "icon-xs",
|
|
1307
|
+
variant: "invisible",
|
|
1308
|
+
"aria-label": "Close",
|
|
1309
|
+
className: "tw:absolute tw:right-2 tw:top-2 tw:z-10",
|
|
1310
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1311
|
+
onClick: onClose,
|
|
1312
|
+
children: /* @__PURE__ */ jsx(X, {})
|
|
1313
|
+
}), filtered.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
928
1314
|
className: "tw:px-4 tw:py-3 tw:text-center tw:text-sm tw:text-muted-foreground",
|
|
929
|
-
children:
|
|
1315
|
+
children: emptyText
|
|
930
1316
|
}) : /* @__PURE__ */ jsx(Command.List, {
|
|
931
|
-
|
|
932
|
-
|
|
1317
|
+
className: "tw:group-data-[variant=argo]/command:px-1 tw:group-data-[variant=argo]/command:pb-1",
|
|
1318
|
+
viewPortClassName: "tw:max-h-[26rem]",
|
|
1319
|
+
children: Object.entries(groups).map(([groupName, groupItems], index) => /* @__PURE__ */ jsx(Command.Group, {
|
|
933
1320
|
heading: groupName,
|
|
1321
|
+
className: cn("tw:[&_[cmdk-group-heading]]:normal-case", "tw:[&_[cmdk-group-heading]]:tracking-normal", "tw:[&_[cmdk-group-heading]]:border-b-0", "tw:[&_[cmdk-group-heading]]:!py-0", "tw:[&_[cmdk-group-heading]]:mt-2", "tw:[&_[cmdk-group-heading]]:mb-1", "tw:[&_[cmdk-group-heading]]:pl-2", "tw:[&_[cmdk-group-heading]]:pr-9", "tw:[&_[cmdk-group-heading]]:text-xs", index > 0 && "tw:mt-3"),
|
|
934
1322
|
children: groupItems.map((item) => /* @__PURE__ */ jsx(Command.Item, {
|
|
935
1323
|
value: item.value,
|
|
936
|
-
|
|
937
|
-
onSelect: () => onSelect(item
|
|
1324
|
+
className: "tw:rounded-md",
|
|
1325
|
+
onSelect: () => onSelect(item),
|
|
938
1326
|
children: /* @__PURE__ */ jsxs("div", {
|
|
939
1327
|
className: "tw:flex tw:min-w-0 tw:flex-1 tw:flex-col",
|
|
940
1328
|
children: [/* @__PURE__ */ jsx("span", { children: item.label }), item.description && /* @__PURE__ */ jsx(Truncated, {
|
|
@@ -945,11 +1333,32 @@ const PromptInputCommands = ({ items, open, onSelect, filterValue, children }) =
|
|
|
945
1333
|
})
|
|
946
1334
|
}, item.value))
|
|
947
1335
|
}, groupName))
|
|
948
|
-
})
|
|
1336
|
+
})]
|
|
949
1337
|
}), children]
|
|
950
1338
|
});
|
|
951
1339
|
};
|
|
952
1340
|
//#endregion
|
|
1341
|
+
//#region src/blocks/AIKit/PromptInputMenuTrigger.tsx
|
|
1342
|
+
/**
|
|
1343
|
+
* The composer's "add to message" button.
|
|
1344
|
+
*
|
|
1345
|
+
* - One glyph for both states: a plus rotated 45° reads as a close cross, so nothing swaps.
|
|
1346
|
+
* - `aria-expanded` mirrors `open`; the accessible name changes with it.
|
|
1347
|
+
* - Wrapped in `PromptInputAction` so a click does not hand focus to the textarea.
|
|
1348
|
+
*/
|
|
1349
|
+
const PromptInputMenuTrigger = ({ open = false, large = false, className, ref, ...props }) => /* @__PURE__ */ jsx(PromptInputAction, { children: /* @__PURE__ */ jsx(Button, {
|
|
1350
|
+
ref,
|
|
1351
|
+
type: "button",
|
|
1352
|
+
size: large ? "icon" : "icon-sm",
|
|
1353
|
+
variant: "invisible",
|
|
1354
|
+
"aria-expanded": open,
|
|
1355
|
+
"aria-label": open ? "Close menu" : "Add to message",
|
|
1356
|
+
"data-testid": "prompt-input-menu-trigger",
|
|
1357
|
+
className: cn("tw:rounded-full", "tw:hover:not-disabled:bg-muted tw:hover:not-disabled:text-foreground tw:hover:not-disabled:border-transparent", "tw:aria-expanded:bg-muted", className),
|
|
1358
|
+
...props,
|
|
1359
|
+
children: /* @__PURE__ */ jsx(Plus, { className: cn(large ? "tw:size-5" : "tw:size-4", "tw:transition-transform tw:duration-300 tw:ease-out tw:motion-reduce:transition-none", open && "tw:rotate-135") })
|
|
1360
|
+
}) });
|
|
1361
|
+
//#endregion
|
|
953
1362
|
//#region src/blocks/AIKit/PromptInputSubmit.tsx
|
|
954
1363
|
const ARIA_LABEL_BY_STATUS = {
|
|
955
1364
|
ready: "Send message",
|
|
@@ -1024,11 +1433,7 @@ const promptInputVariants = cva([
|
|
|
1024
1433
|
"tw:pt-3",
|
|
1025
1434
|
"tw:pb-2"
|
|
1026
1435
|
],
|
|
1027
|
-
inline: [
|
|
1028
|
-
"tw:flex-row",
|
|
1029
|
-
"tw:py-3",
|
|
1030
|
-
"tw:items-center"
|
|
1031
|
-
]
|
|
1436
|
+
inline: ["tw:flex-col", "tw:py-3"]
|
|
1032
1437
|
} }
|
|
1033
1438
|
});
|
|
1034
1439
|
const wrapperVariants = cva(null, {
|
|
@@ -1038,7 +1443,7 @@ const wrapperVariants = cva(null, {
|
|
|
1038
1443
|
inline: ""
|
|
1039
1444
|
} }
|
|
1040
1445
|
});
|
|
1041
|
-
const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, disabled, disableSubmit, value = "", onValueChange, status = "ready", maxRows = 6, minRows, commands, autoFocus, textareaRef: publishedRef, ...textareaProps }) => {
|
|
1446
|
+
const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, attachments, className, disabled, disableSubmit, value = "", onValueChange, status = "ready", maxRows = 6, minRows, commands, autoFocus, textareaRef: publishedRef, ...textareaProps }) => {
|
|
1042
1447
|
const defaultMinRows = variant === "inline" ? 1 : 2;
|
|
1043
1448
|
const isDisabled = (disabled || disableSubmit || !value.trim()) && status !== "streaming" && status !== "interrupted";
|
|
1044
1449
|
const textareaId = useId();
|
|
@@ -1053,10 +1458,11 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, dis
|
|
|
1053
1458
|
if (autoFocusRef.current) textareaRef.current?.focus({ preventScroll: true });
|
|
1054
1459
|
}, []);
|
|
1055
1460
|
if (dismissedValue !== null && value !== dismissedValue) setDismissedValue(null);
|
|
1056
|
-
const
|
|
1461
|
+
const hasMenus = !!commands && commands.length > 0;
|
|
1057
1462
|
const valueLooksLikeSlashCommand = value.startsWith("/") && !/\s/.test(value);
|
|
1058
|
-
const
|
|
1059
|
-
const
|
|
1463
|
+
const menuOpen = hasMenus && valueLooksLikeSlashCommand && value !== dismissedValue;
|
|
1464
|
+
const menuItems = commands ?? [];
|
|
1465
|
+
const menuFilter = value.slice(1);
|
|
1060
1466
|
const handleSubmit = () => {
|
|
1061
1467
|
if (isDisabled) return;
|
|
1062
1468
|
if (status === "streaming") onAbort?.();
|
|
@@ -1070,19 +1476,19 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, dis
|
|
|
1070
1476
|
const handleOnChange = (event) => {
|
|
1071
1477
|
onValueChange?.(event.target.value);
|
|
1072
1478
|
};
|
|
1073
|
-
const
|
|
1074
|
-
onValueChange?.(
|
|
1479
|
+
const handleMenuSelect = (item) => {
|
|
1480
|
+
onValueChange?.(item.value + " ");
|
|
1075
1481
|
textareaRef.current?.focus();
|
|
1076
1482
|
};
|
|
1077
1483
|
const handleOnKeyDown = (event) => {
|
|
1078
|
-
if (
|
|
1484
|
+
if (menuOpen) {
|
|
1079
1485
|
if (event.key === "Escape") {
|
|
1080
1486
|
event.preventDefault();
|
|
1081
1487
|
setDismissedValue(value);
|
|
1082
1488
|
return;
|
|
1083
1489
|
}
|
|
1084
1490
|
if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "Enter") return;
|
|
1085
|
-
} else if (
|
|
1491
|
+
} else if (hasMenus && CMDK_HIJACKED_KEYS.has(event.key) && isCmdkNavigation(event)) event.stopPropagation();
|
|
1086
1492
|
if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey && !event.metaKey) {
|
|
1087
1493
|
event.preventDefault();
|
|
1088
1494
|
if (status !== "streaming") handleSubmit();
|
|
@@ -1100,48 +1506,63 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, dis
|
|
|
1100
1506
|
"data-status": status,
|
|
1101
1507
|
onMouseDown: handleOnMouseDown,
|
|
1102
1508
|
onSubmit: handleOnSubmit,
|
|
1103
|
-
className: cn(promptInputVariants({ variant }), disabled && "tw:bg-none tw:border-border tw:shadow-[inset_0_100vw_rgb(var(--muted))] tw:cursor-default"),
|
|
1509
|
+
className: cn(promptInputVariants({ variant }), disabled && "tw:bg-none tw:border-border tw:shadow-[inset_0_100vw_rgb(var(--muted))] tw:cursor-default", className),
|
|
1104
1510
|
children: [
|
|
1105
|
-
|
|
1106
|
-
className: "tw:flex tw:
|
|
1107
|
-
children:
|
|
1511
|
+
attachments && /* @__PURE__ */ jsx("div", {
|
|
1512
|
+
className: "tw:flex tw:flex-col tw:gap-2 tw:pb-2",
|
|
1513
|
+
children: attachments
|
|
1108
1514
|
}),
|
|
1109
1515
|
/* @__PURE__ */ jsx("label", {
|
|
1110
1516
|
htmlFor: textareaId,
|
|
1111
1517
|
className: "tw:sr-only",
|
|
1112
1518
|
children: "Prompt Input"
|
|
1113
1519
|
}),
|
|
1114
|
-
/* @__PURE__ */ jsx(TextareaAutosize, {
|
|
1115
|
-
...textareaProps,
|
|
1116
|
-
disabled,
|
|
1117
|
-
minRows: minRows ?? defaultMinRows,
|
|
1118
|
-
maxRows,
|
|
1119
|
-
id: textareaId,
|
|
1120
|
-
ref: setTextareaNode,
|
|
1121
|
-
value,
|
|
1122
|
-
onChange: handleOnChange,
|
|
1123
|
-
onKeyDown: handleOnKeyDown,
|
|
1124
|
-
className: cn("tw:resize-none", "tw:outline-hidden", "tw:w-full", "tw:px-2", "tw:bg-transparent", disabled ? "tw:text-muted-foreground" : "tw:text-foreground", "tw:text-sm", "tw:font-sans", "tw:transition-[width]", "tw:delay-75", "tw:ease-linear", "tw:overflow-auto")
|
|
1125
|
-
}),
|
|
1126
1520
|
/* @__PURE__ */ jsxs("div", {
|
|
1127
|
-
className: cn("tw:flex tw:
|
|
1128
|
-
children: [
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1521
|
+
className: cn("tw:flex tw:min-w-0 tw:gap-2", variant === "inline" ? "tw:flex-row tw:items-center" : "tw:flex-col"),
|
|
1522
|
+
children: [
|
|
1523
|
+
prefix && /* @__PURE__ */ jsx("div", {
|
|
1524
|
+
className: "tw:flex tw:shrink-0 tw:items-center",
|
|
1525
|
+
children: prefix
|
|
1526
|
+
}),
|
|
1527
|
+
/* @__PURE__ */ jsx("div", {
|
|
1528
|
+
className: "tw:flex tw:min-w-0 tw:flex-1 tw:items-center",
|
|
1529
|
+
children: /* @__PURE__ */ jsx(TextareaAutosize, {
|
|
1530
|
+
...textareaProps,
|
|
1531
|
+
disabled,
|
|
1532
|
+
minRows: minRows ?? defaultMinRows,
|
|
1533
|
+
maxRows,
|
|
1534
|
+
id: textareaId,
|
|
1535
|
+
ref: setTextareaNode,
|
|
1536
|
+
value,
|
|
1537
|
+
onChange: handleOnChange,
|
|
1538
|
+
onKeyDown: handleOnKeyDown,
|
|
1539
|
+
className: cn("tw:resize-none", "tw:outline-hidden", "tw:min-w-32 tw:flex-1", variant === "inline" ? "tw:px-0" : "tw:pl-2 tw:pr-0", "tw:bg-transparent", disabled ? "tw:text-muted-foreground" : "tw:text-foreground", "tw:text-sm", "tw:font-sans", "tw:transition-[width]", "tw:delay-75", "tw:ease-linear", "tw:overflow-auto")
|
|
1540
|
+
})
|
|
1541
|
+
}),
|
|
1542
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1543
|
+
className: cn("tw:flex tw:shrink-0 tw:items-center tw:gap-1", "tw:[&>*:last-child]:ml-1", variant === "inline" && "tw:pr-2.5"),
|
|
1544
|
+
children: [suffix, /* @__PURE__ */ jsx(PromptInputSubmit, {
|
|
1545
|
+
disabled: isDisabled,
|
|
1546
|
+
status
|
|
1547
|
+
})]
|
|
1548
|
+
})
|
|
1549
|
+
]
|
|
1132
1550
|
})
|
|
1133
1551
|
]
|
|
1134
1552
|
})
|
|
1135
1553
|
});
|
|
1136
|
-
return /* @__PURE__ */ jsx(TooltipProvider, { children:
|
|
1137
|
-
items:
|
|
1138
|
-
open:
|
|
1139
|
-
onSelect:
|
|
1140
|
-
filterValue:
|
|
1554
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { children: hasMenus ? /* @__PURE__ */ jsx(PromptInputCommands, {
|
|
1555
|
+
items: menuItems,
|
|
1556
|
+
open: menuOpen,
|
|
1557
|
+
onSelect: handleMenuSelect,
|
|
1558
|
+
filterValue: menuFilter,
|
|
1559
|
+
emptyText: "No commands found",
|
|
1560
|
+
onClose: () => setDismissedValue(value),
|
|
1141
1561
|
children: formEl
|
|
1142
1562
|
}) : formEl });
|
|
1143
1563
|
};
|
|
1144
1564
|
PromptInput.Action = PromptInputAction;
|
|
1565
|
+
PromptInput.MenuTrigger = PromptInputMenuTrigger;
|
|
1145
1566
|
//#endregion
|
|
1146
1567
|
//#region src/blocks/AIKit/Question.tsx
|
|
1147
1568
|
const handleKeyDown = (callback) => (e) => {
|
|
@@ -1154,6 +1575,10 @@ const Question = ({ question, header, options, multiSelect = true, selectedOptio
|
|
|
1154
1575
|
const [isCustomExpanded, setIsCustomExpanded] = useState(false);
|
|
1155
1576
|
const isCustomSelected = isCustomExpanded || customAnswer.length > 0;
|
|
1156
1577
|
const hasSteps = currentStep != null && totalSteps != null;
|
|
1578
|
+
const showStepControls = hasSteps && totalSteps > 1;
|
|
1579
|
+
const showSkipButton = showSkip && onSkip != null;
|
|
1580
|
+
const showSubmitButton = multiSelect || isCustomSelected;
|
|
1581
|
+
const showFooter = showStepControls || showSkipButton || showSubmitButton;
|
|
1157
1582
|
const progressValue = hasSteps ? currentStep / totalSteps * 100 : 0;
|
|
1158
1583
|
const isFirstStep = currentStep === 1;
|
|
1159
1584
|
const isLastStep = hasSteps && currentStep === totalSteps;
|
|
@@ -1280,14 +1705,14 @@ const Question = ({ question, header, options, multiSelect = true, selectedOptio
|
|
|
1280
1705
|
}),
|
|
1281
1706
|
/* @__PURE__ */ jsxs("div", {
|
|
1282
1707
|
className: "tw:flex tw:flex-col",
|
|
1283
|
-
children: [
|
|
1708
|
+
children: [showStepControls && /* @__PURE__ */ jsx(Progress, {
|
|
1284
1709
|
size: "sm",
|
|
1285
1710
|
value: progressValue,
|
|
1286
1711
|
className: "tw:rounded-none"
|
|
1287
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
1288
|
-
className: cn("tw:flex tw:items-center tw:justify-end tw:gap-4 tw:px-4 tw:py-3", !
|
|
1712
|
+
}), showFooter && /* @__PURE__ */ jsxs("div", {
|
|
1713
|
+
className: cn("tw:flex tw:items-center tw:justify-end tw:gap-4 tw:px-4 tw:py-3", !showStepControls && "tw:border-0 tw:border-t tw:border-solid tw:border-border"),
|
|
1289
1714
|
children: [
|
|
1290
|
-
|
|
1715
|
+
showStepControls && /* @__PURE__ */ jsxs("div", {
|
|
1291
1716
|
className: "tw:flex tw:items-center tw:gap-2",
|
|
1292
1717
|
children: [
|
|
1293
1718
|
/* @__PURE__ */ jsx("button", {
|
|
@@ -1316,14 +1741,14 @@ const Question = ({ question, header, options, multiSelect = true, selectedOptio
|
|
|
1316
1741
|
})
|
|
1317
1742
|
]
|
|
1318
1743
|
}),
|
|
1319
|
-
|
|
1744
|
+
showSkipButton && /* @__PURE__ */ jsx(Button, {
|
|
1320
1745
|
variant: "outline",
|
|
1321
1746
|
size: "small",
|
|
1322
1747
|
onClick: onSkip,
|
|
1323
1748
|
"data-testid": "question-skip-button",
|
|
1324
1749
|
children: "Skip"
|
|
1325
1750
|
}),
|
|
1326
|
-
|
|
1751
|
+
showSubmitButton && (submitTooltip ? /* @__PURE__ */ jsx(Tooltip, {
|
|
1327
1752
|
tooltip: submitTooltip,
|
|
1328
1753
|
placement: "top",
|
|
1329
1754
|
triggerAsChild: true,
|
|
@@ -1656,6 +2081,27 @@ const formatPercentage = (value) => {
|
|
|
1656
2081
|
return `${value < 100 ? Math.min(rounded, 99) : rounded}%`;
|
|
1657
2082
|
};
|
|
1658
2083
|
//#endregion
|
|
2084
|
+
//#region src/blocks/AIKit/UsageBar.tsx
|
|
2085
|
+
/**
|
|
2086
|
+
* One budget, as a labelled row over a bar.
|
|
2087
|
+
*
|
|
2088
|
+
* - The label and the figure sit on one baseline at either end of the row; the bar spans both.
|
|
2089
|
+
* - `value` over 100 or `NaN` is clamped by `clampPercentage` — `Progress`'s indicator is
|
|
2090
|
+
* unclamped and would otherwise slide off the row.
|
|
2091
|
+
*/
|
|
2092
|
+
const UsageBar = ({ value, label, variant, className, ...props }) => /* @__PURE__ */ jsxs("span", {
|
|
2093
|
+
...props,
|
|
2094
|
+
className: cn("tw:flex tw:flex-col tw:gap-1.5 tw:text-xs tw:text-muted-foreground", className),
|
|
2095
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
2096
|
+
className: "tw:flex tw:items-baseline tw:justify-between tw:gap-2",
|
|
2097
|
+
children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", { children: `${formatPercentage(Math.max(0, 100 - value))} left` })]
|
|
2098
|
+
}), /* @__PURE__ */ jsx(Progress, {
|
|
2099
|
+
size: "sm",
|
|
2100
|
+
variant,
|
|
2101
|
+
value: clampPercentage(value)
|
|
2102
|
+
})]
|
|
2103
|
+
});
|
|
2104
|
+
//#endregion
|
|
1659
2105
|
//#region src/blocks/AIKit/UsageStatsRing.tsx
|
|
1660
2106
|
const UsageStatsRing = ({ value, size = 16, strokeWidth = 2.5, className }) => {
|
|
1661
2107
|
const radius = Math.max(0, (size - strokeWidth) / 2 - 1);
|
|
@@ -1698,9 +2144,10 @@ const UsageStats = ({ value, alert = false, children, className, ariaLabel, alig
|
|
|
1698
2144
|
onOpenChange,
|
|
1699
2145
|
children: [/* @__PURE__ */ jsx(HoverCard.Trigger, {
|
|
1700
2146
|
asChild: true,
|
|
1701
|
-
children: /* @__PURE__ */ jsxs("
|
|
2147
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
2148
|
+
type: "button",
|
|
1702
2149
|
"aria-label": ariaLabel ?? defaultLabel,
|
|
1703
|
-
className: cn("tw:relative tw:inline-flex tw:size-8 tw:items-center tw:justify-center", className),
|
|
2150
|
+
className: cn("tw:relative tw:inline-flex tw:size-8 tw:shrink-0 tw:items-center tw:justify-center", "tw:cursor-pointer tw:rounded-full tw:border-0 tw:bg-transparent tw:p-0", "tw:transition-colors tw:hover:bg-muted", "tw:focus-visible:outline-hidden tw:focus-visible:ring-2 tw:focus-visible:ring-ring/50", className),
|
|
1704
2151
|
children: [/* @__PURE__ */ jsx(UsageStatsRing, { value }), hasAlert && /* @__PURE__ */ jsx(AlertCircle, {
|
|
1705
2152
|
"data-testid": "usage-stats-alert",
|
|
1706
2153
|
className: cn("tw:absolute tw:-right-0.5 tw:-top-0.5 tw:size-3", isWarning ? "tw:text-warning-500" : "tw:text-error-500")
|
|
@@ -1715,6 +2162,6 @@ const UsageStats = ({ value, alert = false, children, className, ariaLabel, alig
|
|
|
1715
2162
|
});
|
|
1716
2163
|
};
|
|
1717
2164
|
//#endregion
|
|
1718
|
-
export { AnswerActions, AnswerSummary, CollapsibleStepHeader, DIAGRAM_LANGUAGES, InlineStepDetail, InlineStepLabel, Markdown, MarkdownTable, MessageBubble, PlanApproval, PlanRewritingIndicator, PromptInput, Question, QuestionBlock, SuggestedPrompt, ThinkingIndicator, ToolCallApproval, ToolCallChip, ToolCallStatusHeader, UsageStats, UsageStatsRing, clampPercentage, formatPercentage };
|
|
2165
|
+
export { AnswerActions, AnswerSummary, ChatInlineIcon, CollapsibleStepHeader, DIAGRAM_LANGUAGES, InlineStepDetail, InlineStepGroup, InlineStepLabel, Markdown, MarkdownTable, MessageBubble, PlanApproval, PlanRewritingIndicator, PromptInput, Question, QuestionBlock, SuggestedPrompt, ThinkingIndicator, ToolCallApproval, ToolCallChip, ToolCallStatusHeader, UsageBar, UsageStats, UsageStatsRing, clampPercentage, formatPercentage, getChatInlineIconName };
|
|
1719
2166
|
|
|
1720
|
-
//# sourceMappingURL=AIKit-
|
|
2167
|
+
//# sourceMappingURL=AIKit-ELIsegJz.js.map
|