@keboola/design 13.0.2 → 13.1.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 +2 -0
- package/CHANGELOG.md +23 -0
- package/README.md +2 -2
- package/dist/{AIKit-DDIi0r4w.js → AIKit-BVCWP9Ks.js} +260 -68
- package/dist/{AIKit-DDIi0r4w.js.map → AIKit-BVCWP9Ks.js.map} +1 -1
- package/dist/{CodeEditor-jZMw1ioj.js → CodeEditor-Df7VVx08.js} +2 -2
- package/dist/{CodeEditor-jZMw1ioj.js.map → CodeEditor-Df7VVx08.js.map} +1 -1
- package/dist/Toast-NrOdDDru.js +76 -0
- package/dist/Toast-NrOdDDru.js.map +1 -0
- package/dist/editors.js +1 -1
- package/dist/index.d.ts +46 -12
- package/dist/index.js +5 -4
- package/dist/internal/blocks/AIKit.js +1 -1
- package/dist/internal/components/Toast.js +1 -1
- package/dist/internal/hooks/useAltDigitHotkey.js +51 -0
- package/dist/internal/hooks/useAltDigitHotkey.js.map +1 -0
- package/dist/markdown-editor.d.ts +22 -1
- package/dist/markdown-editor.js +442 -200
- package/dist/markdown-editor.js.map +1 -1
- package/dist/rjsf.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/Toast-PfQf3vj-.js +0 -49
- package/dist/Toast-PfQf3vj-.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -99,6 +99,8 @@ This file answers **which** component to use; [`COMPONENT-API.md`](./COMPONENT-A
|
|
|
99
99
|
|
|
100
100
|
`FileUpload` composes `FileUpload.DropZone` (the dashed box, owning the drag / error visuals) with `FileUpload.FileList` (selected files; `invalid` tints the rows). It deliberately ships **no** rejection copy — the wording is product copy, so read the current rejections with `useFileUploadRejections()` inside `FileUpload` and write the sentence yourself. Reach for `useFileDropTarget` only when there is no box to render: it returns `getRootProps()` / `isDragActive` for an affordance you style, and `noClick` / `noKeyboard` keep an already-interactive subtree (a composer, a form) from opening the file dialog on every click.
|
|
101
101
|
|
|
102
|
+
**A `MarkdownEditor` whose markdown another reader renders** (a reply posted to a ticket, a comment shown with `react-markdown`) takes `autoLink` and `pasteMarkdown`: without them a bare URL exports escaped (`a\_b`) and links elsewhere to the wrong address, and a plain-text paste of a fence, list or table lands one paragraph per line. Leave them off where a stored value is saved back on every `onChange`: `autoLink` exports an escaped stored URL unescaped, so it rewrites that value when it is opened.
|
|
103
|
+
|
|
102
104
|
**`InputGroup.Addon` (`align="inline-start"` / `"inline-end"`) keeps its own content width** — it no longer gives up space to the input, so a badge or a rendered selection stays fully visible instead of being squeezed to an ellipsis. Bound long, caller-controlled content yourself (a `max-w-[…] overflow-hidden` on the addon) if it isn't a fixed-size icon or button. The exception is `TextInput`'s `rightSuffixVariant="addon"` suffix, which shrinks and ellipsizes inline content instead. `InputGroup`'s padding/sizing rules reach any descendant `input`/`textarea`, not just a direct child, so a consumer that wraps the input in its own layout (chips, an overlay) picks up that padding automatically — cancel it with `!px-0`/`!py-0` where the wrapping layout already owns its own edge padding (see `MultiCombobox`).
|
|
103
105
|
|
|
104
106
|
### Status surfaces
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @keboola/design
|
|
2
2
|
|
|
3
|
+
## 13.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- `Question` answers to the keyboard: every option and "Other" carry a number, a digit or Enter picks (a picked key turns into a check), ↑↓ move, ⌘/Ctrl+Enter sends, Space skips. `autoFocus` focuses the card; `enableShortcuts` adds ⌥1–9 from anywhere. New `useAltDigitHotkey` hook. Kai's question card uses both (DES-209)
|
|
8
|
+
|
|
9
|
+
- Kai's follow-ups are message-shaped bubbles (`SuggestedPrompt variant="bubble"`): ↑ from an empty composer enters them, ↑ / ↓ / Tab move, Enter sends and returns the caret (DES-215)
|
|
10
|
+
|
|
11
|
+
- `MarkdownEditor` takes two opt-in props, both off by default and ignored with `singleLine`; with both off the editor and its markdown are unchanged.
|
|
12
|
+
- `autoLink`: a typed, pasted or loaded bare URL (`https://`, `http://`, `www.`) or email address exports as typed, `https://example.com/a_b` instead of `https://example.com/a\_b`, so any CommonMark or GFM reader links the same address, and no `*x*` inside it turns italic. What counts as a URL and where it ends follow GFM's autolink rules (`remark-gfm`): trailing `!"'*,.:;?_~]`, an unbalanced `)` and an entity like `&` stay outside. In the box it becomes a link where Lexical's autolink accepts it, with whitespace or `.,;` on both sides; one right after `(` or `<` still exports as typed but shows as text. Code blocks hold no links and inline code exports as code. A value stored with the old escaping (`https://example.com/a\_b`) loads as the same URL and exports unescaped, so the editor emits `onChange` once, on load, with the healed value.
|
|
13
|
+
- `pasteMarkdown`: a plain-text paste (`text/plain` with no `text/html` and no files) is read as markdown, with the same rules as `value`, and inserted at the selection, so a pasted fence, list, nested list, table or log keeps its shape instead of becoming one paragraph per line. HTML pastes, file pastes and pastes inside a code block keep the current behaviour. One undo removes the whole paste.
|
|
14
|
+
|
|
15
|
+
- `MarkdownEditor` takes `toolbarMode` and starts the caret where the placeholder is.
|
|
16
|
+
- `toolbarMode="fixed"` pins the formatting toolbar to the top of the editor instead of floating it over the selection, so a comment or reply box shows it takes Markdown before anything is selected. It renders in the editor rather than portalled to `document.body`, so it inherits the consumer's token scope.
|
|
17
|
+
- `showToolbar` still decides whether the toolbar shows at all; `toolbarMode` defaults to `floating`, so nothing changes for existing consumers.
|
|
18
|
+
- The placeholder now covers the editor's content box and repeats its padding instead of guessing an offset, and the editable's first and last blocks drop the `prose` margins that `prose` zeroes only for its own direct children. An empty editor's placeholder and caret line up, and non-`compact` content no longer starts 1.25em below the top padding.
|
|
19
|
+
|
|
20
|
+
- `useNotification` takes `remindAfter` (ms): a notification the user dismissed comes back after that long while it is still enabled. A visible notification follows its `title` and `description` when they are strings, and a notification without an `id` keeps one id for the component's lifetime — so unmounting before the first frame no longer dismisses every toast on screen.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- The README's Storybook links point at `design-keboola-engineering.vercel.app`, the Storybook CI publishes on every release.
|
|
25
|
+
|
|
3
26
|
## 13.0.2
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
The Keboola design system — shadcn-aligned, Radix-underneath, Lucide icons.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@keboola/design)
|
|
6
|
-
[](https://design-keboola.vercel.app/)
|
|
6
|
+
[](https://design-keboola-engineering.vercel.app/)
|
|
7
7
|
|
|
8
|
-
📦 **Storybook:** https://design-keboola.vercel.app/
|
|
8
|
+
📦 **Storybook:** https://design-keboola-engineering.vercel.app/
|
|
9
9
|
|
|
10
10
|
📖 **Decision tree:** [`AGENTS.md`](./AGENTS.md) — which component to use when (ships in the tarball).
|
|
11
11
|
🔧 **Prop signatures:** [`COMPONENT-API.md`](./COMPONENT-API.md) — generated, version-pinned (ships in the tarball).
|
|
@@ -9,13 +9,14 @@ import { Truncated } from "./internal/components/Truncated.js";
|
|
|
9
9
|
import { HoverCard } from "./HoverCard-B8OKu9XZ.js";
|
|
10
10
|
import { Command } from "./Command-BQPiZCNz.js";
|
|
11
11
|
import { TextInput } from "./TextInput-BWYXbfs4.js";
|
|
12
|
-
import { Checkbox } from "./Checkbox-8yIIgY7g.js";
|
|
13
12
|
import { Badge } from "./Badge-Dz0cj6rj.js";
|
|
14
13
|
import { Textarea } from "./internal/blocks/Textarea.js";
|
|
15
14
|
import { Table as Table$1 } from "./Table-BaVT5Tkn.js";
|
|
16
15
|
import { DiagramSkeleton } from "./internal/blocks/DiagramShared/DiagramSkeleton.js";
|
|
17
16
|
import { DiagramSource } from "./DiagramSource-C5_d8Gdj.js";
|
|
17
|
+
import { Kbd, keys } from "./Kbd-J4n1J6i4.js";
|
|
18
18
|
import { Progress } from "./internal/components/Progress.js";
|
|
19
|
+
import { digitFromKeyCode, useAltDigitHotkey } from "./internal/hooks/useAltDigitHotkey.js";
|
|
19
20
|
import { Slot } from "@radix-ui/react-slot";
|
|
20
21
|
import { createContext, isValidElement, use, useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
21
22
|
import { cva } from "class-variance-authority";
|
|
@@ -1740,33 +1741,153 @@ PromptInput.Action = PromptInputAction;
|
|
|
1740
1741
|
PromptInput.MenuTrigger = PromptInputMenuTrigger;
|
|
1741
1742
|
//#endregion
|
|
1742
1743
|
//#region src/blocks/AIKit/Question.tsx
|
|
1744
|
+
const isEditableTarget = (target) => target instanceof HTMLElement && (target.isContentEditable || [
|
|
1745
|
+
"INPUT",
|
|
1746
|
+
"TEXTAREA",
|
|
1747
|
+
"SELECT"
|
|
1748
|
+
].includes(target.tagName));
|
|
1749
|
+
const isFieldWithText = (element) => {
|
|
1750
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) return element.value !== "";
|
|
1751
|
+
return element instanceof HTMLElement && element.isContentEditable && !!element.textContent;
|
|
1752
|
+
};
|
|
1753
|
+
const hasCommandModifier = (e) => e.altKey || e.ctrlKey || e.metaKey;
|
|
1754
|
+
const SEND_KEY_CLASS = "tw:!bg-white/20 tw:!text-primary-foreground";
|
|
1755
|
+
const keyRange = (count) => count > 1 ? `1–${count}` : "1";
|
|
1756
|
+
/** An option's key, or a blank slot past the ninth; a picked option's turns into a check. */
|
|
1757
|
+
const OptionKey = ({ digit, checked }) => digit === null && !checked ? /* @__PURE__ */ jsx("span", {
|
|
1758
|
+
"aria-hidden": true,
|
|
1759
|
+
className: "tw:w-5 tw:shrink-0"
|
|
1760
|
+
}) : /* @__PURE__ */ jsx(Kbd, {
|
|
1761
|
+
"aria-hidden": true,
|
|
1762
|
+
className: cn("tw:-my-0.5", checked && "tw:!bg-primary tw:!text-primary-foreground"),
|
|
1763
|
+
"data-testid": "question-option-digit",
|
|
1764
|
+
children: checked ? /* @__PURE__ */ jsx(Check, { className: "tw:size-3 tw:animate-in tw:fade-in tw:zoom-in-50 tw:duration-150" }) : digit
|
|
1765
|
+
});
|
|
1743
1766
|
const handleKeyDown = (callback) => (e) => {
|
|
1767
|
+
if (hasCommandModifier(e) || e.repeat) return;
|
|
1744
1768
|
if (e.key === "Enter" || e.key === " ") {
|
|
1745
1769
|
e.preventDefault();
|
|
1746
1770
|
callback();
|
|
1747
1771
|
}
|
|
1748
1772
|
};
|
|
1749
|
-
const Question = ({ question, header, options, multiSelect = true, selectedOptionIds = [], onSelectionChange, customAnswer = "", onCustomAnswerChange, onCustomAnswerConfirm, showCustomAnswer = true, customAnswerPlaceholder = "Type your answer…", showSkip = true, onSkip, onClose, onNext, submitTooltip, onPrevious, currentStep, totalSteps, renderPreview, className }) => {
|
|
1773
|
+
const Question = ({ question, header, options, multiSelect = true, selectedOptionIds = [], onSelectionChange, customAnswer = "", onCustomAnswerChange, onCustomAnswerConfirm, showCustomAnswer = true, customAnswerPlaceholder = "Type your answer…", showSkip = true, onSkip, onClose, onNext, submitTooltip, onPrevious, currentStep, totalSteps, renderPreview, autoFocus = false, enableShortcuts = false, className }) => {
|
|
1750
1774
|
const [isCustomExpanded, setIsCustomExpanded] = useState(false);
|
|
1775
|
+
const [activeRow, setActiveRow] = useState(0);
|
|
1776
|
+
const [pendingId, setPendingId] = useState(null);
|
|
1777
|
+
const rootRef = useRef(null);
|
|
1778
|
+
const rowsRef = useRef([]);
|
|
1779
|
+
const customInputRef = useRef(null);
|
|
1751
1780
|
const isCustomSelected = isCustomExpanded || customAnswer.length > 0;
|
|
1752
1781
|
const hasSteps = currentStep != null && totalSteps != null;
|
|
1753
1782
|
const showStepControls = hasSteps && totalSteps > 1;
|
|
1754
1783
|
const showSkipButton = showSkip && onSkip != null;
|
|
1755
|
-
const
|
|
1784
|
+
const chosenId = multiSelect ? null : pendingId ?? selectedOptionIds[0] ?? null;
|
|
1785
|
+
const showSubmitButton = multiSelect || isCustomSelected || chosenId !== null;
|
|
1756
1786
|
const showFooter = showStepControls || showSkipButton || showSubmitButton;
|
|
1757
1787
|
const progressValue = hasSteps ? currentStep / totalSteps * 100 : 0;
|
|
1758
1788
|
const isFirstStep = currentStep === 1;
|
|
1759
1789
|
const isLastStep = hasSteps && currentStep === totalSteps;
|
|
1790
|
+
const numberedCount = Math.min(options.length, 9);
|
|
1791
|
+
const otherDigit = showCustomAnswer && options.length < 9 ? options.length + 1 : null;
|
|
1792
|
+
const rowCount = options.length + (showCustomAnswer ? 1 : 0);
|
|
1793
|
+
const tabbableRow = activeRow < rowCount ? activeRow : 0;
|
|
1760
1794
|
const handleOptionToggle = (optionId) => {
|
|
1761
1795
|
if (multiSelect) {
|
|
1762
1796
|
const nextIds = selectedOptionIds.includes(optionId) ? selectedOptionIds.filter((id) => id !== optionId) : [...selectedOptionIds, optionId];
|
|
1763
1797
|
onSelectionChange?.(nextIds);
|
|
1764
1798
|
} else {
|
|
1765
1799
|
setIsCustomExpanded(false);
|
|
1800
|
+
setPendingId(null);
|
|
1766
1801
|
onCustomAnswerChange?.("");
|
|
1767
1802
|
onSelectionChange?.([optionId]);
|
|
1768
1803
|
}
|
|
1769
1804
|
};
|
|
1805
|
+
const handleOptionKey = (optionId) => {
|
|
1806
|
+
if (multiSelect) handleOptionToggle(optionId);
|
|
1807
|
+
else {
|
|
1808
|
+
setIsCustomExpanded(false);
|
|
1809
|
+
onCustomAnswerChange?.("");
|
|
1810
|
+
setPendingId(optionId);
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
useAltDigitHotkey({
|
|
1814
|
+
count: numberedCount,
|
|
1815
|
+
onDigit: (index) => handleOptionKey(options[index].id),
|
|
1816
|
+
enabled: enableShortcuts,
|
|
1817
|
+
ref: rootRef
|
|
1818
|
+
});
|
|
1819
|
+
useEffect(() => {
|
|
1820
|
+
if (!autoFocus || isFieldWithText(document.activeElement)) return;
|
|
1821
|
+
rootRef.current?.focus({ preventScroll: true });
|
|
1822
|
+
}, [autoFocus]);
|
|
1823
|
+
const focusRow = (index) => {
|
|
1824
|
+
setActiveRow(index);
|
|
1825
|
+
rowsRef.current[index]?.focus();
|
|
1826
|
+
};
|
|
1827
|
+
/**
|
|
1828
|
+
* The card's own keys, never fired from a text field inside it; a held key acts once.
|
|
1829
|
+
*
|
|
1830
|
+
* 1. ↑ / ↓ move focus; a digit picks or toggles; Enter on a row selects or toggles it.
|
|
1831
|
+
* 2. ⌘/Ctrl+Enter sends: the selected option (single-select) or the next step; Space skips.
|
|
1832
|
+
* 3. Any other character starts a custom answer: focus moves to "Other", which receives it.
|
|
1833
|
+
* 4. A key the card takes stops here, so page shortcuts (`a`, `/`) never see it.
|
|
1834
|
+
*/
|
|
1835
|
+
const handleCardKeyDown = (e) => {
|
|
1836
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && !e.altKey && !e.repeat) {
|
|
1837
|
+
if (!showSubmitButton) return;
|
|
1838
|
+
send();
|
|
1839
|
+
e.preventDefault();
|
|
1840
|
+
e.stopPropagation();
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
if (e.key === "ArrowUp" && e.target === customInputRef.current && !hasCommandModifier(e)) {
|
|
1844
|
+
e.preventDefault();
|
|
1845
|
+
focusRow(options.length);
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
if (e.defaultPrevented || hasCommandModifier(e) || isEditableTarget(e.target)) return;
|
|
1849
|
+
if ((e.key === "ArrowDown" || e.key === "ArrowUp") && rowCount > 0) {
|
|
1850
|
+
e.preventDefault();
|
|
1851
|
+
const delta = e.key === "ArrowDown" ? 1 : -1;
|
|
1852
|
+
const target = e.target;
|
|
1853
|
+
const current = rowsRef.current.findIndex((row) => target instanceof Node && !!row?.contains(target));
|
|
1854
|
+
focusRow(((current === -1 ? delta > 0 ? -1 : rowCount : current) + delta + rowCount) % rowCount);
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
if (e.repeat) return;
|
|
1858
|
+
const digit = digitFromKeyCode(e.code);
|
|
1859
|
+
if (digit !== null && digit === otherDigit) {
|
|
1860
|
+
e.preventDefault();
|
|
1861
|
+
e.stopPropagation();
|
|
1862
|
+
customInputRef.current?.focus();
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
if (digit !== null && digit <= numberedCount) {
|
|
1866
|
+
e.preventDefault();
|
|
1867
|
+
e.stopPropagation();
|
|
1868
|
+
handleOptionKey(options[digit - 1].id);
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
if (e.key === " " && e.target === e.currentTarget) {
|
|
1872
|
+
e.preventDefault();
|
|
1873
|
+
e.stopPropagation();
|
|
1874
|
+
if (showSkipButton) onSkip?.();
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
if (showCustomAnswer && digit === null && e.key.length === 1 && e.key !== " ") {
|
|
1878
|
+
e.stopPropagation();
|
|
1879
|
+
customInputRef.current?.focus();
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
const send = () => {
|
|
1884
|
+
if (!multiSelect && !isCustomSelected && chosenId) handleOptionToggle(chosenId);
|
|
1885
|
+
else onNext?.();
|
|
1886
|
+
};
|
|
1887
|
+
const handleNextStep = () => {
|
|
1888
|
+
if (!multiSelect && pendingId) handleOptionToggle(pendingId);
|
|
1889
|
+
else onNext?.();
|
|
1890
|
+
};
|
|
1770
1891
|
const handleCustomToggle = (checked) => {
|
|
1771
1892
|
setIsCustomExpanded(checked);
|
|
1772
1893
|
if (checked) {
|
|
@@ -1775,14 +1896,37 @@ const Question = ({ question, header, options, multiSelect = true, selectedOptio
|
|
|
1775
1896
|
};
|
|
1776
1897
|
const handleCustomFocus = () => {
|
|
1777
1898
|
setIsCustomExpanded(true);
|
|
1899
|
+
setPendingId(null);
|
|
1778
1900
|
if (!multiSelect) onSelectionChange?.([]);
|
|
1779
1901
|
};
|
|
1780
1902
|
const handleCustomRowClick = () => {
|
|
1781
1903
|
if (multiSelect) handleCustomToggle(!isCustomSelected);
|
|
1782
1904
|
else handleCustomFocus();
|
|
1783
1905
|
};
|
|
1906
|
+
const handleCustomRowKey = () => {
|
|
1907
|
+
const opens = !multiSelect || !isCustomSelected;
|
|
1908
|
+
handleCustomRowClick();
|
|
1909
|
+
if (opens) customInputRef.current?.focus();
|
|
1910
|
+
};
|
|
1911
|
+
const submitButton = /* @__PURE__ */ jsxs(Button, {
|
|
1912
|
+
variant: "primary",
|
|
1913
|
+
size: "small",
|
|
1914
|
+
onClick: send,
|
|
1915
|
+
"data-testid": "question-next-button",
|
|
1916
|
+
children: [isLastStep ? "Submit" : "Next", /* @__PURE__ */ jsx(Kbd, {
|
|
1917
|
+
"aria-hidden": true,
|
|
1918
|
+
casing: "as-written",
|
|
1919
|
+
className: SEND_KEY_CLASS,
|
|
1920
|
+
children: `${keys.mod.symbol} Enter`
|
|
1921
|
+
})]
|
|
1922
|
+
});
|
|
1784
1923
|
return /* @__PURE__ */ jsxs("div", {
|
|
1785
|
-
|
|
1924
|
+
ref: rootRef,
|
|
1925
|
+
role: "group",
|
|
1926
|
+
"aria-label": question,
|
|
1927
|
+
tabIndex: -1,
|
|
1928
|
+
onKeyDown: handleCardKeyDown,
|
|
1929
|
+
className: cn("tw:flex tw:max-w-[800px] tw:flex-col tw:gap-4 tw:overflow-hidden tw:rounded-xl tw:border tw:border-solid tw:border-border tw:bg-card tw:text-card-foreground tw:shadow-lg tw:outline-none", className),
|
|
1786
1930
|
"data-testid": "question-root",
|
|
1787
1931
|
"data-question-step": currentStep,
|
|
1788
1932
|
"data-question-total-steps": totalSteps,
|
|
@@ -1812,56 +1956,76 @@ const Question = ({ question, header, options, multiSelect = true, selectedOptio
|
|
|
1812
1956
|
className: "tw:px-4",
|
|
1813
1957
|
children: /* @__PURE__ */ jsxs("div", {
|
|
1814
1958
|
className: "tw:flex tw:flex-col tw:gap-4 tw:border-0 tw:border-l tw:border-solid tw:border-primary tw:px-4 tw:py-2",
|
|
1815
|
-
children: [options.map((option) =>
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1959
|
+
children: [options.map((option, index) => {
|
|
1960
|
+
const digit = index < numberedCount ? index + 1 : null;
|
|
1961
|
+
const isSelected = selectedOptionIds.includes(option.id);
|
|
1962
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1963
|
+
ref: (row) => {
|
|
1964
|
+
rowsRef.current[index] = row;
|
|
1965
|
+
},
|
|
1966
|
+
className: "tw:-mx-2 tw:-my-1.5 tw:flex tw:cursor-pointer tw:items-start tw:gap-2 tw:rounded-md tw:px-2 tw:py-1.5 tw:outline-none tw:hover:bg-accent tw:focus-visible:ring-2 tw:focus-visible:ring-ring",
|
|
1967
|
+
onClick: () => handleOptionToggle(option.id),
|
|
1968
|
+
onKeyDown: (e) => {
|
|
1969
|
+
if (hasCommandModifier(e) || e.repeat) return;
|
|
1970
|
+
if (e.key === " " || e.key === "Enter") {
|
|
1971
|
+
e.preventDefault();
|
|
1972
|
+
handleOptionKey(option.id);
|
|
1973
|
+
}
|
|
1974
|
+
},
|
|
1975
|
+
onFocus: () => setActiveRow(index),
|
|
1976
|
+
role: "button",
|
|
1977
|
+
"aria-pressed": multiSelect ? isSelected : chosenId === option.id,
|
|
1978
|
+
tabIndex: index === tabbableRow ? 0 : -1,
|
|
1979
|
+
"aria-keyshortcuts": digit === null ? void 0 : [String(digit), enableShortcuts && `Alt+${digit}`].filter(Boolean).join(" "),
|
|
1980
|
+
"data-question-option": true,
|
|
1981
|
+
"data-testid": `question-option-${option.id}`,
|
|
1982
|
+
children: [/* @__PURE__ */ jsx(OptionKey, {
|
|
1983
|
+
digit,
|
|
1984
|
+
checked: multiSelect ? isSelected : chosenId === option.id
|
|
1985
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1986
|
+
className: "tw:flex tw:min-w-0 tw:flex-1 tw:items-start tw:gap-3",
|
|
1987
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1988
|
+
className: "tw:flex tw:flex-col tw:gap-1.5",
|
|
1989
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1990
|
+
className: "tw:text-sm tw:font-medium tw:leading-none",
|
|
1991
|
+
children: option.label
|
|
1992
|
+
}), option.description && /* @__PURE__ */ jsx("span", {
|
|
1993
|
+
className: "tw:text-sm tw:font-normal tw:leading-5 tw:text-muted-foreground",
|
|
1994
|
+
children: option.description
|
|
1995
|
+
})]
|
|
1996
|
+
}), option.preview && /* @__PURE__ */ jsx("div", {
|
|
1997
|
+
"data-testid": "question-option-preview",
|
|
1998
|
+
className: cn("tw:ml-auto tw:flex tw:max-h-40 tw:min-w-0 tw:max-w-[50%] tw:flex-col tw:overflow-hidden tw:rounded-md", !renderPreview && "tw:overflow-auto tw:border tw:border-solid tw:border-border tw:bg-muted tw:p-2 tw:text-foreground"),
|
|
1999
|
+
children: renderPreview ? renderPreview(option.preview) : /* @__PURE__ */ jsx("pre", {
|
|
2000
|
+
className: "tw:m-0 tw:overflow-visible tw:border-0 tw:bg-transparent tw:p-0 tw:font-mono tw:text-xs tw:leading-5 tw:text-foreground",
|
|
2001
|
+
children: option.preview
|
|
2002
|
+
})
|
|
1837
2003
|
})]
|
|
1838
|
-
}), option.preview && /* @__PURE__ */ jsx("div", {
|
|
1839
|
-
"data-testid": "question-option-preview",
|
|
1840
|
-
className: cn("tw:ml-auto tw:flex tw:max-h-40 tw:min-w-0 tw:max-w-[50%] tw:flex-col tw:overflow-hidden tw:rounded-md", !renderPreview && "tw:overflow-auto tw:border tw:border-solid tw:border-border tw:bg-muted tw:p-2 tw:text-foreground"),
|
|
1841
|
-
children: renderPreview ? renderPreview(option.preview) : /* @__PURE__ */ jsx("pre", {
|
|
1842
|
-
className: "tw:m-0 tw:overflow-visible tw:border-0 tw:bg-transparent tw:p-0 tw:font-mono tw:text-xs tw:leading-5 tw:text-foreground",
|
|
1843
|
-
children: option.preview
|
|
1844
|
-
})
|
|
1845
2004
|
})]
|
|
1846
|
-
})
|
|
1847
|
-
}
|
|
2005
|
+
}, option.id);
|
|
2006
|
+
}), showCustomAnswer && /* @__PURE__ */ jsxs("div", {
|
|
1848
2007
|
className: "tw:flex tw:flex-col tw:gap-2",
|
|
1849
2008
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
1850
|
-
|
|
2009
|
+
ref: (row) => {
|
|
2010
|
+
rowsRef.current[options.length] = row;
|
|
2011
|
+
},
|
|
2012
|
+
className: "tw:-mx-2 tw:-my-1.5 tw:flex tw:cursor-pointer tw:items-start tw:gap-2 tw:rounded-md tw:px-2 tw:py-1.5 tw:outline-none tw:hover:bg-accent tw:focus-visible:ring-2 tw:focus-visible:ring-ring",
|
|
1851
2013
|
onClick: handleCustomRowClick,
|
|
1852
|
-
onKeyDown: handleKeyDown(
|
|
2014
|
+
onKeyDown: handleKeyDown(handleCustomRowKey),
|
|
2015
|
+
onFocus: () => setActiveRow(options.length),
|
|
1853
2016
|
role: "button",
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
2017
|
+
"aria-pressed": isCustomSelected,
|
|
2018
|
+
"aria-keyshortcuts": otherDigit === null ? void 0 : String(otherDigit),
|
|
2019
|
+
tabIndex: options.length === tabbableRow ? 0 : -1,
|
|
2020
|
+
children: [/* @__PURE__ */ jsx(OptionKey, {
|
|
2021
|
+
digit: otherDigit,
|
|
2022
|
+
checked: isCustomSelected
|
|
1860
2023
|
}), /* @__PURE__ */ jsx("span", {
|
|
1861
|
-
className: cn("tw:text-sm tw:font-medium tw:leading-none", !multiSelect && isCustomSelected && "tw:text-primary"),
|
|
2024
|
+
className: cn("tw:flex-1 tw:text-sm tw:font-medium tw:leading-none", !multiSelect && isCustomSelected && "tw:text-primary"),
|
|
1862
2025
|
children: "Other"
|
|
1863
2026
|
})]
|
|
1864
2027
|
}), /* @__PURE__ */ jsx(TextInput, {
|
|
2028
|
+
ref: customInputRef,
|
|
1865
2029
|
value: customAnswer,
|
|
1866
2030
|
onValueChange: (value) => onCustomAnswerChange?.(value),
|
|
1867
2031
|
onFocus: handleCustomFocus,
|
|
@@ -1878,6 +2042,31 @@ const Question = ({ question, header, options, multiSelect = true, selectedOptio
|
|
|
1878
2042
|
})]
|
|
1879
2043
|
})
|
|
1880
2044
|
}),
|
|
2045
|
+
numberedCount > 0 && /* @__PURE__ */ jsxs("div", {
|
|
2046
|
+
"aria-hidden": true,
|
|
2047
|
+
className: "tw:-mt-1 tw:flex tw:flex-wrap tw:items-center tw:gap-x-3 tw:gap-y-1 tw:px-4 tw:text-xs tw:text-muted-foreground",
|
|
2048
|
+
"data-testid": "question-keyboard-hint",
|
|
2049
|
+
children: [
|
|
2050
|
+
/* @__PURE__ */ jsxs("span", {
|
|
2051
|
+
className: "tw:inline-flex tw:items-center tw:gap-1",
|
|
2052
|
+
children: [/* @__PURE__ */ jsx(Kbd, {
|
|
2053
|
+
casing: "as-written",
|
|
2054
|
+
children: keyRange(numberedCount + (otherDigit === null ? 0 : 1))
|
|
2055
|
+
}), multiSelect ? "toggle" : "choose"]
|
|
2056
|
+
}),
|
|
2057
|
+
rowCount > 1 && /* @__PURE__ */ jsxs("span", {
|
|
2058
|
+
className: "tw:inline-flex tw:items-center tw:gap-1",
|
|
2059
|
+
children: [/* @__PURE__ */ jsxs(Kbd, { children: [/* @__PURE__ */ jsx(ArrowUp, { className: "tw:size-3" }), /* @__PURE__ */ jsx(ArrowDown, { className: "tw:size-3" })] }), "move"]
|
|
2060
|
+
}),
|
|
2061
|
+
/* @__PURE__ */ jsxs("span", {
|
|
2062
|
+
className: "tw:inline-flex tw:items-center tw:gap-1",
|
|
2063
|
+
children: [/* @__PURE__ */ jsx(Kbd, {
|
|
2064
|
+
casing: "as-written",
|
|
2065
|
+
children: "Enter"
|
|
2066
|
+
}), multiSelect ? "toggle" : "select"]
|
|
2067
|
+
})
|
|
2068
|
+
]
|
|
2069
|
+
}),
|
|
1881
2070
|
/* @__PURE__ */ jsxs("div", {
|
|
1882
2071
|
className: "tw:flex tw:flex-col",
|
|
1883
2072
|
children: [showStepControls && /* @__PURE__ */ jsx(Progress, {
|
|
@@ -1910,37 +2099,35 @@ const Question = ({ question, header, options, multiSelect = true, selectedOptio
|
|
|
1910
2099
|
type: "button",
|
|
1911
2100
|
disabled: currentStep === totalSteps,
|
|
1912
2101
|
className: "tw:flex tw:cursor-pointer tw:items-center tw:justify-center tw:bg-transparent tw:p-0 tw:disabled:cursor-default tailwindcss-preflight",
|
|
1913
|
-
onClick:
|
|
2102
|
+
onClick: handleNextStep,
|
|
1914
2103
|
"aria-label": "Next step",
|
|
1915
2104
|
children: /* @__PURE__ */ jsx(ChevronRight, { className: cn("tw:size-5", currentStep === totalSteps ? "tw:text-primary/25" : "tw:text-primary") })
|
|
1916
2105
|
})
|
|
1917
2106
|
]
|
|
1918
2107
|
}),
|
|
1919
|
-
showSkipButton && /* @__PURE__ */ jsx(
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
}),
|
|
1926
|
-
showSubmitButton && (submitTooltip ? /* @__PURE__ */ jsx(Tooltip, {
|
|
1927
|
-
tooltip: submitTooltip,
|
|
2108
|
+
showSkipButton && /* @__PURE__ */ jsx(Tooltip, {
|
|
2109
|
+
tooltip: /* @__PURE__ */ jsx(Kbd, {
|
|
2110
|
+
variant: "primary",
|
|
2111
|
+
casing: "as-written",
|
|
2112
|
+
children: "Space"
|
|
2113
|
+
}),
|
|
1928
2114
|
placement: "top",
|
|
1929
2115
|
triggerAsChild: true,
|
|
1930
2116
|
children: /* @__PURE__ */ jsx(Button, {
|
|
1931
|
-
variant: "
|
|
2117
|
+
variant: "outline",
|
|
1932
2118
|
size: "small",
|
|
1933
|
-
onClick:
|
|
1934
|
-
"
|
|
1935
|
-
|
|
2119
|
+
onClick: onSkip,
|
|
2120
|
+
"aria-keyshortcuts": "Space",
|
|
2121
|
+
"data-testid": "question-skip-button",
|
|
2122
|
+
children: "Skip"
|
|
1936
2123
|
})
|
|
1937
|
-
})
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
children:
|
|
1943
|
-
}))
|
|
2124
|
+
}),
|
|
2125
|
+
showSubmitButton && (submitTooltip ? /* @__PURE__ */ jsx(Tooltip, {
|
|
2126
|
+
tooltip: submitTooltip,
|
|
2127
|
+
placement: "top",
|
|
2128
|
+
triggerAsChild: true,
|
|
2129
|
+
children: submitButton
|
|
2130
|
+
}) : submitButton)
|
|
1944
2131
|
]
|
|
1945
2132
|
})]
|
|
1946
2133
|
})
|
|
@@ -1993,11 +2180,16 @@ const QuestionBlock = ({ steps, showSkip = true, onSubmit, onClose, renderPrevie
|
|
|
1993
2180
|
};
|
|
1994
2181
|
//#endregion
|
|
1995
2182
|
//#region src/blocks/AIKit/SuggestedPrompt.tsx
|
|
1996
|
-
const SuggestedPrompt = ({ label, disabled, onClick, icon, className }) => {
|
|
2183
|
+
const SuggestedPrompt = ({ label, disabled, onClick, icon, variant = "row", className }) => {
|
|
1997
2184
|
return /* @__PURE__ */ jsxs("button", {
|
|
1998
2185
|
type: "button",
|
|
1999
2186
|
disabled,
|
|
2000
|
-
|
|
2187
|
+
"data-variant": variant,
|
|
2188
|
+
className: cn("tailwindcss-preflight tw:flex tw:items-center tw:justify-start tw:gap-2.5 tw:whitespace-nowrap", "tw:text-left tw:text-sm tw:text-foreground tw:transition-colors", "tw:focus-visible:outline-none tw:focus-visible:ring-2 tw:focus-visible:ring-ring tw:focus-visible:ring-offset-1", "tw:disabled:cursor-not-allowed tw:disabled:opacity-50", variant === "bubble" ? [
|
|
2189
|
+
"tw:w-fit tw:max-w-[min(85%,40rem)] tw:rounded-xl tw:px-3 tw:py-2",
|
|
2190
|
+
"tw:border tw:border-solid tw:border-border tw:bg-card",
|
|
2191
|
+
"tw:not-disabled:hover:bg-muted tw:focus-visible:bg-muted"
|
|
2192
|
+
] : "tw:rounded-lg tw:px-3 tw:py-2 tw:font-medium tw:not-disabled:hover:bg-accent", className),
|
|
2001
2193
|
onClick,
|
|
2002
2194
|
children: [icon ?? /* @__PURE__ */ jsx(Sparkles, { className: "tw:size-4 tw:shrink-0 tw:basis-4 tw:[&_*]:stroke-aigradient-600" }), /* @__PURE__ */ jsx("div", {
|
|
2003
2195
|
className: "tw:whitespace-normal tw:text-left",
|
|
@@ -2337,4 +2529,4 @@ const UsageStats = ({ value, alert = false, children, className, ariaLabel, alig
|
|
|
2337
2529
|
//#endregion
|
|
2338
2530
|
export { AnswerActions, AnswerSummary, ChatInlineIcon, CollapsibleStepHeader, DIAGRAM_LANGUAGES, FileDownloadChip, InlineStepDetail, InlineStepGroup, InlineStepLabel, Markdown, MarkdownTable, MessageBubble, PROMPT_INPUT_CONTAINER, PlanApproval, PlanRewritingIndicator, PromptInput, Question, QuestionBlock, SuggestedPrompt, ThinkingIndicator, ToolCallApproval, ToolCallChip, ToolCallStatusHeader, UsageBar, UsageStats, UsageStatsRing, clampPercentage, findMentionToken, findSlashToken, formatPercentage, getChatInlineIconName };
|
|
2339
2531
|
|
|
2340
|
-
//# sourceMappingURL=AIKit-
|
|
2532
|
+
//# sourceMappingURL=AIKit-BVCWP9Ks.js.map
|