@juspay/svelte-ui-components 3.4.1 → 3.5.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/README.md +1 -1
- package/dist/Accordion/Accordion.svelte +1 -5
- package/dist/Accordion/properties.d.ts +0 -1
- package/dist/AreaChart/AreaChart.svelte +2 -8
- package/dist/AreaChart/properties.d.ts +0 -10
- package/dist/AttachmentChipRow/AttachmentChipRow.svelte +30 -30
- package/dist/AttachmentChipRow/properties.d.ts +6 -6
- package/dist/Banner/Banner.svelte +1 -5
- package/dist/Banner/properties.d.ts +0 -1
- package/dist/BarChart/BarChart.svelte +7 -13
- package/dist/BarChart/properties.d.ts +1 -9
- package/dist/Book/Book.svelte +1 -5
- package/dist/Book/properties.d.ts +0 -1
- package/dist/Button/Button.svelte +2 -0
- package/dist/Button/properties.d.ts +5 -0
- package/dist/Calendar/Calendar.svelte +3 -11
- package/dist/Calendar/properties.d.ts +0 -11
- package/dist/Chat/Chat.svelte +9 -29
- package/dist/Chat/properties.d.ts +0 -9
- package/dist/Chat/types.d.ts +7 -0
- package/dist/ChatBubble/ChatBubble.svelte +3 -11
- package/dist/ChatBubble/properties.d.ts +0 -3
- package/dist/ChatComposer/ChatComposer.svelte +18 -44
- package/dist/ChatComposer/properties.d.ts +0 -12
- package/dist/ChatHeader/ChatHeader.svelte +1 -5
- package/dist/ChatHeader/properties.d.ts +0 -1
- package/dist/ChatMessage/ChatMessage.svelte +164 -15
- package/dist/ChatMessage/properties.d.ts +34 -3
- package/dist/ChatMessageList/ChatMessageList.svelte +5 -11
- package/dist/ChatMessageList/properties.d.ts +0 -6
- package/dist/ChatSuggestions/ChatSuggestions.svelte +1 -5
- package/dist/ChatSuggestions/properties.d.ts +0 -1
- package/dist/CheckListItem/CheckListItem.svelte +1 -5
- package/dist/CheckListItem/properties.d.ts +0 -1
- package/dist/Checkbox/Checkbox.svelte +1 -5
- package/dist/Checkbox/properties.d.ts +0 -1
- package/dist/ChipInput/ChipInput.svelte +10 -20
- package/dist/ChipInput/properties.d.ts +0 -4
- package/dist/Choicebox/Choicebox.svelte +1 -5
- package/dist/Choicebox/properties.d.ts +0 -1
- package/dist/ColorPicker/ColorPicker.svelte +4 -10
- package/dist/ColorPicker/properties.d.ts +0 -2
- package/dist/Combobox/Combobox.svelte +15 -33
- package/dist/Combobox/properties.d.ts +0 -8
- package/dist/CommandMenu/CommandMenu.svelte +2 -8
- package/dist/CommandMenu/properties.d.ts +0 -2
- package/dist/ContextMenu/ContextMenu.svelte +3 -11
- package/dist/ContextMenu/properties.d.ts +0 -3
- package/dist/DateRangePicker/DateRangePicker.svelte +6 -20
- package/dist/DateRangePicker/properties.d.ts +0 -19
- package/dist/Draggable/Draggable.svelte +7 -7
- package/dist/Draggable/properties.d.ts +3 -3
- package/dist/DualAxisBarChart/DualAxisBarChart.svelte +1 -5
- package/dist/DualAxisBarChart/properties.d.ts +0 -4
- package/dist/FileDropzoneTrigger/FileDropzoneTrigger.svelte +1 -5
- package/dist/FileDropzoneTrigger/properties.d.ts +0 -1
- package/dist/FileInput/FileInput.svelte +2 -8
- package/dist/FileInput/properties.d.ts +0 -2
- package/dist/FunnelChart/FunnelChart.svelte +2 -8
- package/dist/FunnelChart/properties.d.ts +0 -8
- package/dist/Gallery/Gallery.svelte +15 -15
- package/dist/Gallery/properties.d.ts +6 -6
- package/dist/HITL/HITL.svelte +10 -8
- package/dist/HITL/properties.d.ts +2 -2
- package/dist/IframeViewer/IframeViewer.svelte +3 -2
- package/dist/IframeViewer/properties.d.ts +1 -1
- package/dist/Img/Img.svelte +119 -39
- package/dist/Img/properties.d.ts +0 -1
- package/dist/Input/Input.svelte +22 -37
- package/dist/Input/properties.d.ts +3 -10
- package/dist/InputButton/InputButton.svelte +2 -2
- package/dist/LineChart/LineChart.svelte +4 -10
- package/dist/LineChart/properties.d.ts +1 -11
- package/dist/ListItem/ListItem.svelte +10 -22
- package/dist/ListItem/properties.d.ts +5 -10
- package/dist/LottiePlayer/LottiePlayer.svelte +2 -8
- package/dist/LottiePlayer/properties.d.ts +0 -2
- package/dist/MediaPlayer/MediaPlayer.svelte +1 -5
- package/dist/MediaPlayer/properties.d.ts +0 -1
- package/dist/MediaUpload/MediaUpload.svelte +6 -6
- package/dist/MediaUpload/properties.d.ts +3 -3
- package/dist/Menu/Menu.svelte +3 -11
- package/dist/Menu/properties.d.ts +0 -3
- package/dist/Modal/Modal.svelte +15 -31
- package/dist/Modal/properties.d.ts +5 -11
- package/dist/Pagination/Pagination.svelte +3 -7
- package/dist/Pagination/properties.d.ts +1 -2
- package/dist/PieChart/PieChart.svelte +4 -10
- package/dist/PieChart/properties.d.ts +1 -9
- package/dist/Pill/Pill.svelte +1 -5
- package/dist/Pill/properties.d.ts +0 -1
- package/dist/Radio/Radio.svelte +1 -5
- package/dist/Radio/properties.d.ts +0 -1
- package/dist/Resizable/Resizable.svelte +3 -11
- package/dist/Resizable/properties.d.ts +0 -3
- package/dist/SankeyChart/SankeyChart.svelte +4 -14
- package/dist/SankeyChart/properties.d.ts +0 -12
- package/dist/Scroller/Scroller.svelte +1 -5
- package/dist/Scroller/properties.d.ts +0 -1
- package/dist/Select/Select.svelte +3 -11
- package/dist/Select/properties.d.ts +0 -3
- package/dist/Sheet/Sheet.svelte +3 -11
- package/dist/Sheet/properties.d.ts +0 -3
- package/dist/Slider/Slider.svelte +2 -8
- package/dist/Slider/properties.d.ts +0 -2
- package/dist/Snippet/Snippet.svelte +1 -5
- package/dist/Snippet/properties.d.ts +0 -1
- package/dist/SplitButton/SplitButton.svelte +1 -5
- package/dist/SplitButton/properties.d.ts +0 -1
- package/dist/SplitInput/SplitInput.svelte +6 -14
- package/dist/SplitInput/properties.d.ts +0 -3
- package/dist/StatCard/StatCard.svelte +2 -2
- package/dist/StatCard/properties.d.ts +1 -1
- package/dist/Status/Status.svelte +2 -6
- package/dist/Status/properties.d.ts +1 -2
- package/dist/Stepper/Stepper.svelte +2 -12
- package/dist/Stepper/properties.d.ts +1 -14
- package/dist/Table/BuiltinCell.svelte +1 -1
- package/dist/Table/Table.svelte +16 -17
- package/dist/Table/properties.d.ts +3 -35
- package/dist/Tabs/Tabs.svelte +2 -8
- package/dist/Tabs/properties.d.ts +0 -2
- package/dist/TaskList/TaskList.svelte +1 -4
- package/dist/TaskList/properties.d.ts +0 -1
- package/dist/ThemeSwitcher/ThemeSwitcher.svelte +1 -5
- package/dist/ThemeSwitcher/properties.d.ts +0 -1
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte +4 -10
- package/dist/ThinkingIndicator/properties.d.ts +1 -3
- package/dist/Toast/Toast.svelte +2 -2
- package/dist/Toast/properties.d.ts +1 -1
- package/dist/Toggle/Toggle.svelte +1 -5
- package/dist/Toggle/properties.d.ts +0 -1
- package/dist/ToolCallLog/ToolCallLog.svelte +1 -10
- package/dist/ToolCallLog/properties.d.ts +0 -1
- package/dist/Toolbar/Toolbar.svelte +2 -6
- package/dist/Toolbar/properties.d.ts +1 -2
- package/dist/TypewriterText/TypewriterText.svelte +3 -3
- package/dist/TypewriterText/properties.d.ts +1 -1
- package/dist-codemod/bin.js +32 -0
- package/dist-codemod/cli.js +175 -0
- package/dist-codemod/legacy-pairs.js +228 -0
- package/dist-codemod/transform.js +278 -0
- package/dist-codemod/wc-children.js +66 -0
- package/dist-wc/index.js +4629 -5218
- package/package.json +9 -7
- package/scripts/postinstall.mjs +14 -12
- package/dist/deprecation.d.ts +0 -42
- package/dist/deprecation.js +0 -57
- package/scripts/codemod/README.md +0 -76
- package/scripts/codemod/cli.ts +0 -206
- package/scripts/codemod/map.ts +0 -118
- package/scripts/codemod/transform.ts +0 -549
- package/scripts/codemod/tsconfig.json +0 -15
- package/scripts/codemod/wc-children.ts +0 -75
|
@@ -26,18 +26,12 @@
|
|
|
26
26
|
richVideoTooltip,
|
|
27
27
|
richRemoveIcon,
|
|
28
28
|
richFileIcon,
|
|
29
|
-
onremoverichimage
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
onopenrichimage: onopenrichimageLegacy,
|
|
36
|
-
onOpenRichImage,
|
|
37
|
-
onopenrichvideo: onopenrichvideoLegacy,
|
|
38
|
-
onOpenRichVideo,
|
|
39
|
-
onopenrichfile: onopenrichfileLegacy,
|
|
40
|
-
onOpenRichFile,
|
|
29
|
+
onremoverichimage,
|
|
30
|
+
onremoverichfile,
|
|
31
|
+
onremoverichvideo,
|
|
32
|
+
onopenrichimage,
|
|
33
|
+
onopenrichvideo,
|
|
34
|
+
onopenrichfile,
|
|
41
35
|
sendable = null,
|
|
42
36
|
accept = '',
|
|
43
37
|
multiple = false,
|
|
@@ -52,21 +46,15 @@
|
|
|
52
46
|
actionIcon,
|
|
53
47
|
actionLabel = 'Voice conversation',
|
|
54
48
|
leading,
|
|
55
|
-
onsubmit
|
|
56
|
-
onSubmit,
|
|
49
|
+
onsubmit,
|
|
57
50
|
oninput,
|
|
58
51
|
onkeydown,
|
|
59
52
|
onpaste,
|
|
60
|
-
onstop
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
onAttach,
|
|
66
|
-
onattachclick: onattachclickLegacy,
|
|
67
|
-
onAttachClick,
|
|
68
|
-
onaction: onactionLegacy,
|
|
69
|
-
onAction,
|
|
53
|
+
onstop,
|
|
54
|
+
onvoice,
|
|
55
|
+
onattach,
|
|
56
|
+
onattachclick,
|
|
57
|
+
onaction,
|
|
70
58
|
testId,
|
|
71
59
|
inputTestId,
|
|
72
60
|
inputAriaLabel,
|
|
@@ -79,20 +67,6 @@
|
|
|
79
67
|
classes
|
|
80
68
|
}: ChatComposerProperties = $props();
|
|
81
69
|
|
|
82
|
-
// Event-casing phase 1: both spellings accepted, the correct one wins.
|
|
83
|
-
const onaction = $derived(onAction ?? onactionLegacy);
|
|
84
|
-
const onattach = $derived(onAttach ?? onattachLegacy);
|
|
85
|
-
const onattachclick = $derived(onAttachClick ?? onattachclickLegacy);
|
|
86
|
-
const onopenrichfile = $derived(onOpenRichFile ?? onopenrichfileLegacy);
|
|
87
|
-
const onopenrichimage = $derived(onOpenRichImage ?? onopenrichimageLegacy);
|
|
88
|
-
const onopenrichvideo = $derived(onOpenRichVideo ?? onopenrichvideoLegacy);
|
|
89
|
-
const onremoverichfile = $derived(onRemoveRichFile ?? onremoverichfileLegacy);
|
|
90
|
-
const onremoverichimage = $derived(onRemoveRichImage ?? onremoverichimageLegacy);
|
|
91
|
-
const onremoverichvideo = $derived(onRemoveRichVideo ?? onremoverichvideoLegacy);
|
|
92
|
-
const onstop = $derived(onStop ?? onstopLegacy);
|
|
93
|
-
const onsubmit = $derived(onSubmit ?? onsubmitLegacy);
|
|
94
|
-
const onvoice = $derived(onVoice ?? onvoiceLegacy);
|
|
95
|
-
|
|
96
70
|
let fileInput: HTMLInputElement | null = $state(null);
|
|
97
71
|
|
|
98
72
|
let showVoice = $derived(typeof onvoice === 'function');
|
|
@@ -170,12 +144,12 @@
|
|
|
170
144
|
images={richImages}
|
|
171
145
|
videos={richVideos}
|
|
172
146
|
files={richFiles}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
147
|
+
onremoveimage={onremoverichimage}
|
|
148
|
+
onremovevideo={onremoverichvideo}
|
|
149
|
+
onremovefile={onremoverichfile}
|
|
150
|
+
onopenimage={onopenrichimage}
|
|
151
|
+
onopenvideo={onopenrichvideo}
|
|
152
|
+
onopenfile={onopenrichfile}
|
|
179
153
|
imageTooltip={richImageTooltip}
|
|
180
154
|
videoTooltip={richVideoTooltip}
|
|
181
155
|
removeIcon={richRemoveIcon}
|
|
@@ -73,49 +73,37 @@ export type OptionalChatComposerProperties = {
|
|
|
73
73
|
};
|
|
74
74
|
export type ChatComposerEventProperties = {
|
|
75
75
|
onsubmit?: (value: string, attachments: File[]) => void;
|
|
76
|
-
onSubmit?: (value: string, attachments: File[]) => void;
|
|
77
76
|
oninput?: (value: string, event: Event) => void;
|
|
78
77
|
onkeydown?: (event: KeyboardEvent) => void;
|
|
79
78
|
onpaste?: (event: ClipboardEvent) => void;
|
|
80
79
|
onstop?: () => void;
|
|
81
|
-
onStop?: () => void;
|
|
82
80
|
onvoice?: () => void;
|
|
83
|
-
onVoice?: () => void;
|
|
84
81
|
onattach?: (files: File[]) => void;
|
|
85
|
-
onAttach?: (files: File[]) => void;
|
|
86
82
|
/**
|
|
87
83
|
* Intercepts the attach button: when provided, clicking it calls this instead of
|
|
88
84
|
* opening the built-in file picker — for apps with their own chooser (camera /
|
|
89
85
|
* gallery / files).
|
|
90
86
|
*/
|
|
91
87
|
onattachclick?: () => void;
|
|
92
|
-
onAttachClick?: () => void;
|
|
93
88
|
/**
|
|
94
89
|
* Removal callbacks for the rich strip's chips. Omit them to render the rich
|
|
95
90
|
* chips read-only (no remove buttons), mirroring AttachmentChipRow.
|
|
96
91
|
*/
|
|
97
92
|
onremoverichimage?: (id: string) => void;
|
|
98
|
-
onRemoveRichImage?: (id: string) => void;
|
|
99
93
|
onremoverichfile?: (id: string) => void;
|
|
100
|
-
onRemoveRichFile?: (id: string) => void;
|
|
101
94
|
onremoverichvideo?: (id: string) => void;
|
|
102
|
-
onRemoveRichVideo?: (id: string) => void;
|
|
103
95
|
/**
|
|
104
96
|
* Open/preview callbacks for the rich strip's chips — when provided, a chip's
|
|
105
97
|
* tile becomes a real button and clicking it fires with the attachment (e.g.
|
|
106
98
|
* open a lightbox, play the video).
|
|
107
99
|
*/
|
|
108
100
|
onopenrichimage?: (image: AttachmentChipImage) => void;
|
|
109
|
-
onOpenRichImage?: (image: AttachmentChipImage) => void;
|
|
110
101
|
onopenrichvideo?: (video: AttachmentChipVideo) => void;
|
|
111
|
-
onOpenRichVideo?: (video: AttachmentChipVideo) => void;
|
|
112
102
|
onopenrichfile?: (file: AttachmentChipFile) => void;
|
|
113
|
-
onOpenRichFile?: (file: AttachmentChipFile) => void;
|
|
114
103
|
/**
|
|
115
104
|
* The composer's idle action (e.g. voice conversation mode). While the value is
|
|
116
105
|
* empty, nothing is sendable and no reply is streaming, the send button gives way
|
|
117
106
|
* to an action button that fires this.
|
|
118
107
|
*/
|
|
119
108
|
onaction?: () => void;
|
|
120
|
-
onAction?: () => void;
|
|
121
109
|
};
|
|
@@ -14,16 +14,12 @@
|
|
|
14
14
|
closeIcon,
|
|
15
15
|
closeLabel = 'Close',
|
|
16
16
|
showClose,
|
|
17
|
-
onclose
|
|
18
|
-
onClose,
|
|
17
|
+
onclose,
|
|
19
18
|
children,
|
|
20
19
|
testId,
|
|
21
20
|
classes
|
|
22
21
|
}: ChatHeaderProperties = $props();
|
|
23
22
|
|
|
24
|
-
// Event-casing phase 1: both spellings accepted, the correct one wins.
|
|
25
|
-
const onclose = $derived(onClose ?? oncloseLegacy);
|
|
26
|
-
|
|
27
23
|
let displayClose = $derived(showClose ?? typeof onclose === 'function');
|
|
28
24
|
</script>
|
|
29
25
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import Button from '../Button/Button.svelte';
|
|
3
3
|
import LoadingDots from '../LoadingDots/LoadingDots.svelte';
|
|
4
|
+
import TypewriterText from '../TypewriterText/TypewriterText.svelte';
|
|
4
5
|
import copySvg from '../assets/copy.svg?raw';
|
|
5
6
|
import checkmarkSvg from '../assets/checkmark.svg?raw';
|
|
6
7
|
import retrySvg from '../assets/retry.svg?raw';
|
|
@@ -17,6 +18,10 @@
|
|
|
17
18
|
markdown,
|
|
18
19
|
body,
|
|
19
20
|
streaming = false,
|
|
21
|
+
typewriter = false,
|
|
22
|
+
typewriterSpeed,
|
|
23
|
+
clampLines = 0,
|
|
24
|
+
marker = false,
|
|
20
25
|
status,
|
|
21
26
|
avatar,
|
|
22
27
|
header,
|
|
@@ -25,22 +30,48 @@
|
|
|
25
30
|
actions,
|
|
26
31
|
copyLabel = 'Copy',
|
|
27
32
|
retryLabel = 'Retry',
|
|
33
|
+
expandLabel = 'Expand message',
|
|
34
|
+
collapseLabel = 'Collapse message',
|
|
28
35
|
feedbackUpLabel = 'Good response',
|
|
29
36
|
feedbackDownLabel = 'Bad response',
|
|
30
|
-
onretry
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
onFeedback,
|
|
34
|
-
oncopy: oncopyLegacy,
|
|
35
|
-
onCopy,
|
|
37
|
+
onretry,
|
|
38
|
+
onfeedback,
|
|
39
|
+
oncopy,
|
|
36
40
|
testId,
|
|
37
41
|
classes
|
|
38
42
|
}: ChatMessageProperties = $props();
|
|
39
43
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
// A long message can be collapsed to a few lines and opened by click or keyboard.
|
|
45
|
+
// Consumers had been rebuilding this around the `body` snippet; it belongs here so
|
|
46
|
+
// the chrome, the clamp and the expanded state stay one component's concern.
|
|
47
|
+
let expanded = $state(false);
|
|
48
|
+
let clampable = $derived(typeof clampLines === 'number' && clampLines > 0);
|
|
49
|
+
let clampActive = $derived(clampable && !expanded);
|
|
50
|
+
// Turning clamping off must not leave the message stuck open: without this, a parent
|
|
51
|
+
// that sets clampLines to 0 and later restores it would get an already-expanded bubble
|
|
52
|
+
// instead of the clamped default. A prop going away cannot be observed from a $derived
|
|
53
|
+
// without writing to state, so this is the sanctioned escape hatch rather than a
|
|
54
|
+
// reactive convenience.
|
|
55
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
56
|
+
$effect(() => {
|
|
57
|
+
if (!clampable) {
|
|
58
|
+
expanded = false;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
// Ties the toggle to the region it expands. Per-instance, so two clamped messages
|
|
62
|
+
// on one page never collide.
|
|
63
|
+
const uid = $props.id();
|
|
64
|
+
const bodyId = `chat-message-body-${uid}`;
|
|
65
|
+
// Spread rather than a ternary: Button's testId is `string | undefined` and the
|
|
66
|
+
// repo forbids the `undefined` literal, so the prop is omitted instead of blanked.
|
|
67
|
+
let clampToggleTestId = $derived(
|
|
68
|
+
typeof testId === 'string' ? { testId: `${testId}-clamp-toggle` } : {}
|
|
69
|
+
);
|
|
70
|
+
const toggleExpanded = () => {
|
|
71
|
+
if (clampable) {
|
|
72
|
+
expanded = !expanded;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
44
75
|
|
|
45
76
|
let party = $derived(partyOf(role));
|
|
46
77
|
|
|
@@ -75,8 +106,40 @@
|
|
|
75
106
|
hasMarkdown && renderMarkdownFn !== null ? renderMarkdownFn(markdown ?? '') : html
|
|
76
107
|
);
|
|
77
108
|
let hasHtml = $derived(typeof effectiveHtml === 'string' && effectiveHtml.length > 0);
|
|
109
|
+
|
|
78
110
|
let hasBody = $derived(typeof body === 'function');
|
|
79
|
-
|
|
111
|
+
|
|
112
|
+
/* The typewriter reveals SOURCE text, not rendered HTML: typing a pre-rendered
|
|
113
|
+
`html` string character by character would put its tags on screen. So it drives
|
|
114
|
+
off `markdown` or `content` and leaves an `html`-only message to the normal
|
|
115
|
+
path. `markdown` is typed through the same pipeline the static branch uses, so
|
|
116
|
+
rich text reveals as rich text rather than as raw syntax that reformats at the
|
|
117
|
+
end. */
|
|
118
|
+
let typewriterSource = $derived(hasMarkdown ? (markdown ?? '') : content);
|
|
119
|
+
/* Gated on the pipeline for a markdown source. The renderer loads asynchronously, and
|
|
120
|
+
without this the typewriter starts on the SOURCE: `**bold**` is typed literally and
|
|
121
|
+
then reformats when the module resolves. Waiting matches what the static branch
|
|
122
|
+
already does — degrade to the `html`/`content` fallback until the renderer is there —
|
|
123
|
+
rather than showing syntax the consumer never asked to display. */
|
|
124
|
+
let typewriterActive = $derived(
|
|
125
|
+
typewriter &&
|
|
126
|
+
!hasBody &&
|
|
127
|
+
typewriterSource.length > 0 &&
|
|
128
|
+
(!hasMarkdown || renderMarkdownFn !== null)
|
|
129
|
+
);
|
|
130
|
+
let typewriterRenderText = $derived(
|
|
131
|
+
hasMarkdown && renderMarkdownFn !== null ? { renderText: renderMarkdownFn } : {}
|
|
132
|
+
);
|
|
133
|
+
// Clamped at the boundary the way LoadingDots clamps `dots`, so the prop's floor is part
|
|
134
|
+
// of this component's contract rather than something a consumer discovers. The finite
|
|
135
|
+
// check is not redundant: `typeof NaN === 'number'`, and `Math.max(1, NaN)` is `NaN`, so
|
|
136
|
+
// without it the documented floor is not actually enforced.
|
|
137
|
+
let typewriterPacing = $derived(
|
|
138
|
+
typeof typewriterSpeed === 'number' && Number.isFinite(typewriterSpeed)
|
|
139
|
+
? { speed: Math.max(1, typewriterSpeed) }
|
|
140
|
+
: {}
|
|
141
|
+
);
|
|
142
|
+
let hasContent = $derived(hasBody || hasHtml || content.length > 0 || typewriterActive);
|
|
80
143
|
let showTyping = $derived(streaming && !hasContent);
|
|
81
144
|
let showRetry = $derived(typeof onretry === 'function');
|
|
82
145
|
let showFeedback = $derived(typeof onfeedback === 'function');
|
|
@@ -136,14 +199,64 @@
|
|
|
136
199
|
<div class="header">{@render header()}</div>
|
|
137
200
|
{/if}
|
|
138
201
|
|
|
139
|
-
|
|
202
|
+
<!-- The bubble deliberately carries no role and no handler of its own. A button has
|
|
203
|
+
presentational children, so making the bubble one would flatten the paragraphs and
|
|
204
|
+
lists a message body renders; and a click target that is not focusable is a control
|
|
205
|
+
keyboard users cannot reach. The Button below is the whole control. -->
|
|
206
|
+
<div
|
|
207
|
+
class="bubble"
|
|
208
|
+
class:clampable
|
|
209
|
+
class:has-marker={marker}
|
|
210
|
+
style={clampable ? `--chat-message-clamp-lines-prop: ${clampLines};` : null}
|
|
211
|
+
data-clamped={clampActive ? 'true' : null}
|
|
212
|
+
data-expanded={clampable ? String(expanded) : null}
|
|
213
|
+
>
|
|
214
|
+
{#if marker}
|
|
215
|
+
<span class="marker" aria-hidden="true"></span>
|
|
216
|
+
{/if}
|
|
217
|
+
|
|
140
218
|
{#if hasBody}
|
|
141
|
-
<div class="body">{@render body?.()}</div>
|
|
219
|
+
<div class="body" id={bodyId}>{@render body?.()}</div>
|
|
220
|
+
{:else if typewriterActive}
|
|
221
|
+
<!-- ChatMessageList is a role="log" aria-live="polite" region, so a body whose text
|
|
222
|
+
grows one character at a time would be announced as it grows. Silence it only
|
|
223
|
+
WHILE it is growing: leaving it off once streaming ends would suppress the
|
|
224
|
+
settled message too, which is the announcement the live region exists for.
|
|
225
|
+
`aria-live="off"` is the spec answer (the innermost live setting wins), and
|
|
226
|
+
`aria-busy` is the purpose-built one — "being modified, wait before exposing" —
|
|
227
|
+
so the two together do not depend on a single behaviour being implemented. -->
|
|
228
|
+
<div
|
|
229
|
+
class="body"
|
|
230
|
+
id={bodyId}
|
|
231
|
+
aria-live={streaming ? 'off' : null}
|
|
232
|
+
aria-busy={streaming ? 'true' : null}
|
|
233
|
+
>
|
|
234
|
+
<TypewriterText
|
|
235
|
+
text={typewriterSource}
|
|
236
|
+
isStreaming={streaming}
|
|
237
|
+
{...typewriterRenderText}
|
|
238
|
+
{...typewriterPacing}
|
|
239
|
+
/>
|
|
240
|
+
</div>
|
|
142
241
|
{:else if hasHtml}
|
|
143
242
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
144
|
-
<div class="body">{@html effectiveHtml}</div>
|
|
243
|
+
<div class="body" id={bodyId}>{@html effectiveHtml}</div>
|
|
145
244
|
{:else if content.length > 0}
|
|
146
|
-
<div class="body text">{content}</div>
|
|
245
|
+
<div class="body text" id={bodyId}>{content}</div>
|
|
246
|
+
{/if}
|
|
247
|
+
|
|
248
|
+
{#if clampable}
|
|
249
|
+
<div class="clamp-toggle">
|
|
250
|
+
<Button
|
|
251
|
+
variant="ghost"
|
|
252
|
+
size="sm"
|
|
253
|
+
onclick={toggleExpanded}
|
|
254
|
+
ariaExpanded={expanded}
|
|
255
|
+
ariaControls={bodyId}
|
|
256
|
+
text={expanded ? collapseLabel : expandLabel}
|
|
257
|
+
{...clampToggleTestId}
|
|
258
|
+
/>
|
|
259
|
+
</div>
|
|
147
260
|
{/if}
|
|
148
261
|
|
|
149
262
|
{#if showTyping}
|
|
@@ -264,6 +377,27 @@
|
|
|
264
377
|
overflow-wrap: anywhere;
|
|
265
378
|
}
|
|
266
379
|
|
|
380
|
+
/* Only the marker variant becomes a containing block: making every bubble relative
|
|
381
|
+
would silently re-base any absolutely-positioned content a consumer renders in a
|
|
382
|
+
body snippet. */
|
|
383
|
+
.bubble.has-marker {
|
|
384
|
+
position: relative;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.marker {
|
|
388
|
+
/* Decorative and absolutely positioned, so it sits over the bubble's leading
|
|
389
|
+
padding — where a link at the start of a responder message also sits. Without
|
|
390
|
+
this it silently eats those clicks. */
|
|
391
|
+
pointer-events: none;
|
|
392
|
+
position: absolute;
|
|
393
|
+
inset-block: var(--chat-message-marker-inset-block, 0);
|
|
394
|
+
/* Logical, so the bar follows `role` alignment into RTL instead of pinning left. */
|
|
395
|
+
inset-inline-start: var(--chat-message-marker-offset, 0px);
|
|
396
|
+
width: var(--chat-message-marker-width, 2px);
|
|
397
|
+
border-radius: var(--chat-message-marker-border-radius, 0);
|
|
398
|
+
background: var(--chat-message-marker-color, #6d28d9);
|
|
399
|
+
}
|
|
400
|
+
|
|
267
401
|
.chat-message.party-sender .bubble {
|
|
268
402
|
color: var(--chat-message-sender-color, #ffffff);
|
|
269
403
|
background: var(--chat-message-sender-background, #18181b);
|
|
@@ -341,6 +475,21 @@
|
|
|
341
475
|
}
|
|
342
476
|
}
|
|
343
477
|
|
|
478
|
+
.clamp-toggle {
|
|
479
|
+
display: flex;
|
|
480
|
+
margin-top: var(--chat-message-clamp-toggle-margin-top, 4px);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* Clamp the rendered body, not the source: the message keeps its markup and its
|
|
484
|
+
copy text, and expanding is a state change rather than a re-render. */
|
|
485
|
+
.bubble[data-clamped='true'] .body {
|
|
486
|
+
display: -webkit-box;
|
|
487
|
+
line-clamp: var(--chat-message-clamp-lines, var(--chat-message-clamp-lines-prop, 2));
|
|
488
|
+
-webkit-line-clamp: var(--chat-message-clamp-lines, var(--chat-message-clamp-lines-prop, 2));
|
|
489
|
+
-webkit-box-orient: vertical;
|
|
490
|
+
overflow: hidden;
|
|
491
|
+
}
|
|
492
|
+
|
|
344
493
|
.body :global(p) {
|
|
345
494
|
margin: var(--chat-message-paragraph-margin, 0 0 0.5em 0);
|
|
346
495
|
}
|
|
@@ -27,7 +27,35 @@ export type OptionalChatMessageProperties = {
|
|
|
27
27
|
* action still has something to copy.
|
|
28
28
|
*/
|
|
29
29
|
body?: Snippet | null;
|
|
30
|
+
/**
|
|
31
|
+
* Collapse the rendered body to this many lines and render a `Button` below it
|
|
32
|
+
* that expands and re-collapses the message; that button is the only control.
|
|
33
|
+
* `0` or omitted leaves the message uncollapsed and renders no control.
|
|
34
|
+
* The clamp applies to the rendered body, so `content` — and therefore the copy
|
|
35
|
+
* action — always carries the whole message. A consumer stylesheet setting
|
|
36
|
+
* `--chat-message-clamp-lines` takes priority over this value, so a theme can set
|
|
37
|
+
* the line count for every message without touching each call site.
|
|
38
|
+
*/
|
|
39
|
+
clampLines?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Draw an accent bar along the bubble's leading edge — the quote-bar shape a chat
|
|
42
|
+
* surface uses to mark one party's turns without giving them a filled bubble.
|
|
43
|
+
* Decorative: it carries no text and is hidden from assistive technology, so the
|
|
44
|
+
* distinction it makes must also be carried by `role`, which it is.
|
|
45
|
+
*/
|
|
46
|
+
marker?: boolean;
|
|
30
47
|
streaming?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Reveal the message progressively instead of all at once, using `TypewriterText`.
|
|
50
|
+
* Drives off `markdown` or `content` — never `html`, since typing a pre-rendered
|
|
51
|
+
* string would put its tags on screen — and types `markdown` through the same
|
|
52
|
+
* pipeline the static branch uses, so rich text reveals as rich text. `streaming`
|
|
53
|
+
* decides the mode: while it is true, text keeps typing as it grows; the moment it
|
|
54
|
+
* turns false the remainder is shown at once. Ignored when a `body` snippet is set.
|
|
55
|
+
*/
|
|
56
|
+
typewriter?: boolean;
|
|
57
|
+
/** Milliseconds between characters while typing, clamped to a minimum of 1. A non-finite value is ignored. Defaults to `TypewriterText`'s own. */
|
|
58
|
+
typewriterSpeed?: number;
|
|
31
59
|
status?: ChatMessageStatus;
|
|
32
60
|
avatar?: Snippet;
|
|
33
61
|
header?: Snippet;
|
|
@@ -36,6 +64,12 @@ export type OptionalChatMessageProperties = {
|
|
|
36
64
|
actions?: Snippet;
|
|
37
65
|
copyLabel?: string;
|
|
38
66
|
retryLabel?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Label on the expand/collapse button rendered when `clampLines` is set, swapped
|
|
69
|
+
* on the expanded state.
|
|
70
|
+
*/
|
|
71
|
+
expandLabel?: string;
|
|
72
|
+
collapseLabel?: string;
|
|
39
73
|
feedbackUpLabel?: string;
|
|
40
74
|
feedbackDownLabel?: string;
|
|
41
75
|
testId?: string;
|
|
@@ -43,9 +77,6 @@ export type OptionalChatMessageProperties = {
|
|
|
43
77
|
};
|
|
44
78
|
export type ChatMessageEventProperties = {
|
|
45
79
|
onretry?: (() => void) | null;
|
|
46
|
-
onRetry?: (() => void) | null;
|
|
47
80
|
onfeedback?: ((value: ChatMessageFeedback) => void) | null;
|
|
48
|
-
onFeedback?: ((value: ChatMessageFeedback) => void) | null;
|
|
49
81
|
oncopy?: (text: string) => void;
|
|
50
|
-
onCopy?: (text: string) => void;
|
|
51
82
|
};
|
|
@@ -23,21 +23,13 @@
|
|
|
23
23
|
jumpLabel = 'Jump to latest',
|
|
24
24
|
jumpIcon,
|
|
25
25
|
allowCopy = false,
|
|
26
|
-
onscrollstate
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
onRetry,
|
|
30
|
-
onfeedback: onfeedbackLegacy,
|
|
31
|
-
onFeedback,
|
|
26
|
+
onscrollstate,
|
|
27
|
+
onretry,
|
|
28
|
+
onfeedback,
|
|
32
29
|
testId,
|
|
33
30
|
classes
|
|
34
31
|
}: ChatMessageListProperties = $props();
|
|
35
32
|
|
|
36
|
-
// Event-casing phase 1: both spellings accepted, the correct one wins.
|
|
37
|
-
const onfeedback = $derived(onFeedback ?? onfeedbackLegacy);
|
|
38
|
-
const onretry = $derived(onRetry ?? onretryLegacy);
|
|
39
|
-
const onscrollstate = $derived(onScrollState ?? onscrollstateLegacy);
|
|
40
|
-
|
|
41
33
|
let listEl: HTMLElement | null = $state(null);
|
|
42
34
|
let innerEl: HTMLElement | null = $state(null);
|
|
43
35
|
let atBottom = $state(true);
|
|
@@ -311,6 +303,8 @@
|
|
|
311
303
|
markdown={msg.markdown}
|
|
312
304
|
body={typeof messageBody === 'function' ? bodyFor : null}
|
|
313
305
|
streaming={msg.streaming}
|
|
306
|
+
typewriter={msg.typewriter}
|
|
307
|
+
typewriterSpeed={msg.typewriterSpeed}
|
|
314
308
|
status={msg.status}
|
|
315
309
|
allowCopy={allowCopy && partyOf(msg.role) === 'responder'}
|
|
316
310
|
attachments={typeof messageAttachments === 'function' ? attachmentsFor : null}
|
|
@@ -49,12 +49,6 @@ export type ChatMessageListEventProperties = {
|
|
|
49
49
|
atBottom: boolean;
|
|
50
50
|
scrollable: boolean;
|
|
51
51
|
}) => void;
|
|
52
|
-
onScrollState?: (state: {
|
|
53
|
-
atBottom: boolean;
|
|
54
|
-
scrollable: boolean;
|
|
55
|
-
}) => void;
|
|
56
52
|
onretry?: () => void;
|
|
57
|
-
onRetry?: () => void;
|
|
58
53
|
onfeedback?: (value: ChatMessageFeedback, message: ChatMessageData) => void;
|
|
59
|
-
onFeedback?: (value: ChatMessageFeedback, message: ChatMessageData) => void;
|
|
60
54
|
};
|
|
@@ -13,15 +13,11 @@
|
|
|
13
13
|
loading = false,
|
|
14
14
|
icon,
|
|
15
15
|
chipClasses,
|
|
16
|
-
onselect
|
|
17
|
-
onSelect,
|
|
16
|
+
onselect,
|
|
18
17
|
testId,
|
|
19
18
|
classes
|
|
20
19
|
}: ChatSuggestionsProperties = $props();
|
|
21
20
|
|
|
22
|
-
// Event-casing phase 1: both spellings accepted, the correct one wins.
|
|
23
|
-
const onselect = $derived(onSelect ?? onselectLegacy);
|
|
24
|
-
|
|
25
21
|
const labelOf = (item: ChatSuggestion): string => (typeof item === 'string' ? item : item.label);
|
|
26
22
|
|
|
27
23
|
const valueOf = (item: ChatSuggestion): string => {
|
|
@@ -8,14 +8,10 @@
|
|
|
8
8
|
disabled = false,
|
|
9
9
|
checkboxLabel,
|
|
10
10
|
testId,
|
|
11
|
-
onclick
|
|
12
|
-
onClick,
|
|
11
|
+
onclick,
|
|
13
12
|
classes
|
|
14
13
|
}: CheckListItemProperties = $props();
|
|
15
14
|
|
|
16
|
-
// Event-casing phase 1: both spellings accepted, the correct one wins.
|
|
17
|
-
const onclick = $derived(onClick ?? onclickLegacy);
|
|
18
|
-
|
|
19
15
|
function handleClick(value: boolean): void {
|
|
20
16
|
checked = value;
|
|
21
17
|
onclick?.(checked);
|
|
@@ -11,16 +11,12 @@
|
|
|
11
11
|
testId,
|
|
12
12
|
checkedIcon,
|
|
13
13
|
indeterminateIcon,
|
|
14
|
-
onclick
|
|
15
|
-
onClick,
|
|
14
|
+
onclick,
|
|
16
15
|
classes,
|
|
17
16
|
ariaControls,
|
|
18
17
|
ariaLabel
|
|
19
18
|
}: CheckboxProperties = $props();
|
|
20
19
|
|
|
21
|
-
// Event-casing phase 1: both spellings accepted, the correct one wins.
|
|
22
|
-
const onclick = $derived(onClick ?? onclickLegacy);
|
|
23
|
-
|
|
24
20
|
function handleClick(): void {
|
|
25
21
|
if (disabled) {
|
|
26
22
|
return;
|
|
@@ -11,22 +11,12 @@
|
|
|
11
11
|
editable = false,
|
|
12
12
|
testId,
|
|
13
13
|
classes,
|
|
14
|
-
onadd
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
onedit: oneditLegacy,
|
|
19
|
-
onEdit,
|
|
20
|
-
onchange: onchangeLegacy,
|
|
21
|
-
onChange
|
|
14
|
+
onadd,
|
|
15
|
+
ondismiss,
|
|
16
|
+
onedit,
|
|
17
|
+
onchange
|
|
22
18
|
}: ChipInputProperties = $props();
|
|
23
19
|
|
|
24
|
-
// Event-casing phase 1: both spellings accepted, the correct one wins.
|
|
25
|
-
const onadd = $derived(onAdd ?? onaddLegacy);
|
|
26
|
-
const onchange = $derived(onChange ?? onchangeLegacy);
|
|
27
|
-
const ondismiss = $derived(onDismiss ?? ondismissLegacy);
|
|
28
|
-
const onedit = $derived(onEdit ?? oneditLegacy);
|
|
29
|
-
|
|
30
20
|
let draft = $state('');
|
|
31
21
|
|
|
32
22
|
// The chip currently swapped for an inline edit field, held by VALUE rather than by slot
|
|
@@ -153,11 +143,11 @@
|
|
|
153
143
|
actionInput={false}
|
|
154
144
|
classes="chip-input-edit"
|
|
155
145
|
bind:this={editInputRef}
|
|
156
|
-
|
|
146
|
+
oninput={(nextValue) => {
|
|
157
147
|
editDraft = nextValue;
|
|
158
148
|
}}
|
|
159
|
-
|
|
160
|
-
|
|
149
|
+
onkeydown={(event) => handleEditKeyDown(event, chip)}
|
|
150
|
+
onblur={() => handleEditBlur(chip)}
|
|
161
151
|
{...typeof testId === 'string' ? { testId: `${testId}-item-${index}-edit` } : {}}
|
|
162
152
|
/>
|
|
163
153
|
</div>
|
|
@@ -186,11 +176,11 @@
|
|
|
186
176
|
actionInput={false}
|
|
187
177
|
disable={disabled}
|
|
188
178
|
classes="chip-input-draft"
|
|
189
|
-
|
|
179
|
+
oninput={(nextValue) => {
|
|
190
180
|
draft = nextValue;
|
|
191
181
|
}}
|
|
192
|
-
|
|
193
|
-
|
|
182
|
+
onkeydown={handleKeyDown}
|
|
183
|
+
onblur={addChip}
|
|
194
184
|
{...typeof testId === 'string' ? { testId: `${testId}-add` } : {}}
|
|
195
185
|
/>
|
|
196
186
|
</div>
|
|
@@ -34,16 +34,12 @@ export type OptionalChipInputProperties = {
|
|
|
34
34
|
};
|
|
35
35
|
export type ChipInputEventProperties = {
|
|
36
36
|
onadd?: (value: string) => void;
|
|
37
|
-
onAdd?: (value: string) => void;
|
|
38
37
|
ondismiss?: (value: string) => void;
|
|
39
|
-
onDismiss?: (value: string) => void;
|
|
40
38
|
/**
|
|
41
39
|
* Fires after an in-place edit (see `editable`) commits a value that actually changed. Not
|
|
42
40
|
* fired when the edit is cancelled (Escape) or committed with the text unchanged.
|
|
43
41
|
*/
|
|
44
42
|
onedit?: (value: string, previousValue: string) => void;
|
|
45
|
-
onEdit?: (value: string, previousValue: string) => void;
|
|
46
43
|
/** Fires alongside `onadd`/`ondismiss`/`onedit`, after any of them has already updated `values`. */
|
|
47
44
|
onchange?: (values: string[]) => void;
|
|
48
|
-
onChange?: (values: string[]) => void;
|
|
49
45
|
};
|