@schlenkr/quassel 0.6.0 → 0.7.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/ChatInputToolbar.d.ts +6 -10
- package/ComposerPrimaryAction.d.ts +13 -0
- package/QuasselProvider.d.ts +1 -0
- package/README.md +29 -1
- package/RecordingAura.d.ts +4 -0
- package/chat.css +162 -3
- package/index.d.ts +1 -1
- package/index.js +371 -107
- package/index.js.map +4 -4
- package/options.d.ts +12 -0
- package/package.json +1 -1
- package/texts.d.ts +5 -0
package/index.js
CHANGED
|
@@ -42,6 +42,11 @@ var englishTexts = {
|
|
|
42
42
|
send: "Send",
|
|
43
43
|
sendIntoRun: "Feed into the running turn",
|
|
44
44
|
stop: "Stop work",
|
|
45
|
+
microphone: "Speak",
|
|
46
|
+
microphoneHint: "Hold to speak or click to start recording",
|
|
47
|
+
microphonePreparing: "Connecting microphone ...",
|
|
48
|
+
microphoneRecording: "Finish recording",
|
|
49
|
+
microphoneTranscribing: "Transcribing ...",
|
|
45
50
|
placeholder: "Write a message ...",
|
|
46
51
|
steeringPlaceholder: "Interject ...",
|
|
47
52
|
steered: "Fed into the running turn",
|
|
@@ -112,6 +117,11 @@ var germanTexts = {
|
|
|
112
117
|
send: "Senden",
|
|
113
118
|
sendIntoRun: "In die laufende Antwort einreihen",
|
|
114
119
|
stop: "Bearbeitung stoppen",
|
|
120
|
+
microphone: "Sprechen",
|
|
121
|
+
microphoneHint: "Zum Sprechen halten oder zum Aufnehmen antippen",
|
|
122
|
+
microphonePreparing: "Mikrofon wird verbunden ...",
|
|
123
|
+
microphoneRecording: "Aufnahme beenden",
|
|
124
|
+
microphoneTranscribing: "Wird transkribiert ...",
|
|
115
125
|
placeholder: "Nachricht schreiben ...",
|
|
116
126
|
steeringPlaceholder: "Nachsteuern ...",
|
|
117
127
|
steered: "In die laufende Antwort eingereiht",
|
|
@@ -1860,18 +1870,20 @@ function MessageAttachments({ message, texts }) {
|
|
|
1860
1870
|
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1861
1871
|
/* @__PURE__ */ jsx13("ul", { className: "qsl:mt-2.5 qsl:flex qsl:flex-wrap qsl:gap-2", "aria-label": texts.attachments, children: message.attachments.map((attachment, index) => {
|
|
1862
1872
|
const isImage = attachment.mediaType.startsWith("image/");
|
|
1863
|
-
|
|
1864
|
-
|
|
1873
|
+
if (isImage)
|
|
1874
|
+
return /* @__PURE__ */ jsx13("li", { className: "qsl:relative qsl:overflow-hidden qsl:rounded-lg qsl:border qsl:border-border", children: /* @__PURE__ */ jsx13(
|
|
1865
1875
|
"button",
|
|
1866
1876
|
{
|
|
1867
1877
|
type: "button",
|
|
1868
|
-
className: "qsl:block qsl:
|
|
1878
|
+
className: "qsl:block qsl:cursor-zoom-in qsl:bg-secondary",
|
|
1869
1879
|
"aria-label": fillText(texts.enlargeImage ?? defaultTexts.enlargeImage, { name: attachment.name }),
|
|
1880
|
+
title: attachment.name,
|
|
1870
1881
|
onClick: () => setEnlarged(attachment),
|
|
1871
1882
|
"data-chat": "attachment-image",
|
|
1872
|
-
children: /* @__PURE__ */ jsx13("img", { className: "qsl:
|
|
1883
|
+
children: /* @__PURE__ */ jsx13("img", { className: "qsl:block qsl:size-24 qsl:object-cover", src: attachment.url, alt: attachment.name, loading: "lazy" })
|
|
1873
1884
|
}
|
|
1874
|
-
),
|
|
1885
|
+
) }, `${attachment.url}-${index}`);
|
|
1886
|
+
return /* @__PURE__ */ jsxs6("li", { className: "qsl:relative qsl:flex qsl:w-[190px] qsl:max-w-full qsl:min-w-0 qsl:flex-col qsl:overflow-hidden qsl:rounded-lg qsl:border qsl:border-border qsl:text-small", children: [
|
|
1875
1887
|
attachment.mediaType.startsWith("video/") && /* @__PURE__ */ jsx13("video", { className: "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondary qsl:object-contain", src: attachment.url, controls: true, preload: "metadata", "aria-label": attachment.name }),
|
|
1876
1888
|
/* @__PURE__ */ jsxs6("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:[overflow-wrap:anywhere]", children: [
|
|
1877
1889
|
/* @__PURE__ */ jsx13("a", { className: "qsl:font-medium", href: attachmentDownloadUrl(attachment.url), download: attachment.name, children: attachment.name }),
|
|
@@ -1986,7 +1998,7 @@ function ChatPanel({
|
|
|
1986
1998
|
}
|
|
1987
1999
|
|
|
1988
2000
|
// packages/quassel/src/ChatInputToolbar.tsx
|
|
1989
|
-
import { PaperclipIcon, SendIcon, XIcon as XIcon3 } from "lucide-react";
|
|
2001
|
+
import { PaperclipIcon, SendIcon as SendIcon2, XIcon as XIcon3 } from "lucide-react";
|
|
1990
2002
|
|
|
1991
2003
|
// packages/quassel/src/ui/useFileInput.ts
|
|
1992
2004
|
import { useRef as useRef5, useState as useState6 } from "react";
|
|
@@ -2037,9 +2049,214 @@ function useFileInput({ disabled, onFiles, onPasteText }) {
|
|
|
2037
2049
|
}
|
|
2038
2050
|
|
|
2039
2051
|
// packages/quassel/src/ChatInputToolbar.tsx
|
|
2040
|
-
import { useCallback as useCallback3, useContext as useContext4, useEffect as
|
|
2052
|
+
import { useCallback as useCallback3, useContext as useContext4, useEffect as useEffect6, useImperativeHandle, useLayoutEffect as useLayoutEffect5, useRef as useRef8, useState as useState8 } from "react";
|
|
2053
|
+
import { createPortal as createPortal3 } from "react-dom";
|
|
2054
|
+
|
|
2055
|
+
// packages/quassel/src/ComposerPrimaryAction.tsx
|
|
2056
|
+
import { LoaderCircleIcon, MicIcon, SendIcon, SquareIcon as SquareIcon2 } from "lucide-react";
|
|
2057
|
+
import { useEffect as useEffect5, useLayoutEffect as useLayoutEffect4, useRef as useRef7, useState as useState7 } from "react";
|
|
2058
|
+
|
|
2059
|
+
// packages/quassel/src/RecordingAura.tsx
|
|
2060
|
+
import { useLayoutEffect as useLayoutEffect3, useRef as useRef6 } from "react";
|
|
2041
2061
|
import { createPortal as createPortal2 } from "react-dom";
|
|
2042
|
-
import {
|
|
2062
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2063
|
+
function RecordingAura({ anchor, level = 0 }) {
|
|
2064
|
+
const aura = useRef6(null);
|
|
2065
|
+
const volume = Number.isFinite(level) ? Math.max(0, Math.min(1, level)) : 0;
|
|
2066
|
+
useLayoutEffect3(() => {
|
|
2067
|
+
const element = aura.current;
|
|
2068
|
+
const view = anchor.ownerDocument.defaultView;
|
|
2069
|
+
if (!element || !view) return;
|
|
2070
|
+
let frame = 0;
|
|
2071
|
+
let visible = true;
|
|
2072
|
+
const observer = new view.IntersectionObserver(([entry]) => {
|
|
2073
|
+
visible = entry.isIntersecting;
|
|
2074
|
+
});
|
|
2075
|
+
observer.observe(anchor);
|
|
2076
|
+
const update = () => {
|
|
2077
|
+
const rect = anchor.getBoundingClientRect();
|
|
2078
|
+
const style = view.getComputedStyle(anchor);
|
|
2079
|
+
element.style.left = `${rect.left}px`;
|
|
2080
|
+
element.style.top = `${rect.top}px`;
|
|
2081
|
+
element.style.width = `${rect.width}px`;
|
|
2082
|
+
element.style.height = `${rect.height}px`;
|
|
2083
|
+
element.style.visibility = visible && rect.width && rect.height && anchor.checkVisibility() ? "visible" : "hidden";
|
|
2084
|
+
element.style.setProperty("--qsl-pill-aura-color", style.getPropertyValue("--qsl-pill-aura-color") || style.getPropertyValue("--qsl-destructive"));
|
|
2085
|
+
element.style.setProperty("--qsl-pill-aura-ring-color", style.getPropertyValue("--qsl-pill-aura-ring-color") || style.getPropertyValue("--qsl-pill-aura-color") || style.getPropertyValue("--qsl-destructive"));
|
|
2086
|
+
element.style.zIndex = style.getPropertyValue("--qsl-pill-aura-z-index") || "1000";
|
|
2087
|
+
frame = view.requestAnimationFrame(update);
|
|
2088
|
+
};
|
|
2089
|
+
update();
|
|
2090
|
+
return () => {
|
|
2091
|
+
view.cancelAnimationFrame(frame);
|
|
2092
|
+
observer.disconnect();
|
|
2093
|
+
};
|
|
2094
|
+
}, [anchor]);
|
|
2095
|
+
return createPortal2(/* @__PURE__ */ jsx15(
|
|
2096
|
+
"span",
|
|
2097
|
+
{
|
|
2098
|
+
"aria-hidden": "true",
|
|
2099
|
+
className: "qsl:recording-aura",
|
|
2100
|
+
"data-quassel": "",
|
|
2101
|
+
"data-recording-aura": "",
|
|
2102
|
+
ref: aura,
|
|
2103
|
+
style: { transform: `scale(${1.2 + volume * 0.65})`, opacity: 0.4 + volume * 0.4 }
|
|
2104
|
+
}
|
|
2105
|
+
), anchor.ownerDocument.body);
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// packages/quassel/src/ComposerPrimaryAction.tsx
|
|
2109
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2110
|
+
function ComposerPrimaryAction({ hasContent, running: running2, sending, disabled, sendDisabled, microphone, texts, onSend, onStop }) {
|
|
2111
|
+
const state = microphone && microphone.state !== "idle" ? microphone.state : hasContent ? "send" : running2 ? "stop" : microphone && !sending ? "microphone" : "send";
|
|
2112
|
+
const voice = state === "microphone" || state === "preparing" || state === "recording";
|
|
2113
|
+
const busy = state === "preparing" || state === "transcribing";
|
|
2114
|
+
const blocked = disabled || state === "transcribing" || state === "send" && (sendDisabled || sending || !hasContent) || state === "stop" && !onStop;
|
|
2115
|
+
const label = state === "microphone" ? microphone?.label ?? texts.microphone : state === "preparing" ? texts.microphonePreparing : state === "recording" ? texts.microphoneRecording : state === "transcribing" ? texts.microphoneTranscribing : state === "stop" ? texts.stop : running2 ? texts.sendIntoRun : texts.send;
|
|
2116
|
+
const [button, setButton] = useState7(null);
|
|
2117
|
+
const press = useRef7(null);
|
|
2118
|
+
const suppressClick = useRef7(false);
|
|
2119
|
+
const clickReset = useRef7(void 0);
|
|
2120
|
+
const currentMicrophone = useRef7(microphone);
|
|
2121
|
+
const resetClick = () => {
|
|
2122
|
+
clearTimeout(clickReset.current);
|
|
2123
|
+
clickReset.current = setTimeout(() => {
|
|
2124
|
+
suppressClick.current = false;
|
|
2125
|
+
}, 0);
|
|
2126
|
+
};
|
|
2127
|
+
const cancel = () => {
|
|
2128
|
+
const active = press.current;
|
|
2129
|
+
press.current = null;
|
|
2130
|
+
if (active) resetClick();
|
|
2131
|
+
const current = currentMicrophone.current;
|
|
2132
|
+
if (active) active.microphone.onCancel();
|
|
2133
|
+
else if (current?.state === "recording" || current?.state === "preparing") current.onCancel();
|
|
2134
|
+
};
|
|
2135
|
+
useLayoutEffect4(() => {
|
|
2136
|
+
currentMicrophone.current = microphone;
|
|
2137
|
+
}, [microphone]);
|
|
2138
|
+
useEffect5(() => {
|
|
2139
|
+
if (disabled) cancel();
|
|
2140
|
+
}, [disabled]);
|
|
2141
|
+
useEffect5(() => {
|
|
2142
|
+
if (!button) return;
|
|
2143
|
+
const document2 = button.ownerDocument;
|
|
2144
|
+
const view = document2.defaultView;
|
|
2145
|
+
const onVisibility = () => {
|
|
2146
|
+
if (document2.hidden) cancel();
|
|
2147
|
+
};
|
|
2148
|
+
const onEscape = (event) => {
|
|
2149
|
+
if (event.key === "Escape" && (press.current || currentMicrophone.current?.state === "recording" || currentMicrophone.current?.state === "preparing")) {
|
|
2150
|
+
event.preventDefault();
|
|
2151
|
+
cancel();
|
|
2152
|
+
}
|
|
2153
|
+
};
|
|
2154
|
+
view.addEventListener("blur", cancel);
|
|
2155
|
+
document2.addEventListener("visibilitychange", onVisibility);
|
|
2156
|
+
document2.addEventListener("keydown", onEscape);
|
|
2157
|
+
return () => {
|
|
2158
|
+
view.removeEventListener("blur", cancel);
|
|
2159
|
+
document2.removeEventListener("visibilitychange", onVisibility);
|
|
2160
|
+
document2.removeEventListener("keydown", onEscape);
|
|
2161
|
+
cancel();
|
|
2162
|
+
clearTimeout(clickReset.current);
|
|
2163
|
+
};
|
|
2164
|
+
}, [button]);
|
|
2165
|
+
const start = (source, timeStamp, pointerId, key) => {
|
|
2166
|
+
if (!voice || state === "preparing" || blocked || !microphone || press.current) return;
|
|
2167
|
+
press.current = { source, pointerId, key, microphone };
|
|
2168
|
+
clearTimeout(clickReset.current);
|
|
2169
|
+
suppressClick.current = true;
|
|
2170
|
+
microphone.onPressStart({ source, timeStamp });
|
|
2171
|
+
};
|
|
2172
|
+
const end = (timeStamp) => {
|
|
2173
|
+
const active = press.current;
|
|
2174
|
+
press.current = null;
|
|
2175
|
+
resetClick();
|
|
2176
|
+
active?.microphone.onPressEnd({ source: active.source, timeStamp });
|
|
2177
|
+
};
|
|
2178
|
+
const activationKey = (event) => (event.key === " " || event.key === "Enter") && !event.nativeEvent.isComposing && event.nativeEvent.keyCode !== 229;
|
|
2179
|
+
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
2180
|
+
state === "recording" && button && /* @__PURE__ */ jsx16(RecordingAura, { anchor: button, level: microphone?.level }),
|
|
2181
|
+
/* @__PURE__ */ jsxs8(
|
|
2182
|
+
"button",
|
|
2183
|
+
{
|
|
2184
|
+
"aria-busy": busy || void 0,
|
|
2185
|
+
"aria-label": label,
|
|
2186
|
+
"aria-pressed": voice ? state === "recording" : void 0,
|
|
2187
|
+
className: "qsl:primary-action",
|
|
2188
|
+
"data-primary-action": state,
|
|
2189
|
+
disabled: blocked,
|
|
2190
|
+
onBlur: () => {
|
|
2191
|
+
if (press.current?.source === "keyboard") cancel();
|
|
2192
|
+
},
|
|
2193
|
+
onClick: (event) => {
|
|
2194
|
+
if (suppressClick.current) {
|
|
2195
|
+
suppressClick.current = false;
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
if (voice && event.detail === 0) {
|
|
2199
|
+
start("virtual", event.timeStamp);
|
|
2200
|
+
end(event.timeStamp);
|
|
2201
|
+
suppressClick.current = false;
|
|
2202
|
+
} else if (state === "send") onSend();
|
|
2203
|
+
else if (state === "stop") onStop?.();
|
|
2204
|
+
},
|
|
2205
|
+
onContextMenu: (event) => {
|
|
2206
|
+
if (voice) event.preventDefault();
|
|
2207
|
+
},
|
|
2208
|
+
onKeyDown: (event) => {
|
|
2209
|
+
if (activationKey(event) && event.repeat) {
|
|
2210
|
+
event.preventDefault();
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
if (!voice) {
|
|
2214
|
+
suppressClick.current = false;
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
if (activationKey(event)) {
|
|
2218
|
+
event.preventDefault();
|
|
2219
|
+
start("keyboard", event.timeStamp, void 0, event.key);
|
|
2220
|
+
}
|
|
2221
|
+
},
|
|
2222
|
+
onKeyUp: (event) => {
|
|
2223
|
+
if (activationKey(event) && (voice || press.current?.source === "keyboard")) {
|
|
2224
|
+
event.preventDefault();
|
|
2225
|
+
if (press.current?.source === "keyboard" && press.current.key === event.key) end(event.timeStamp);
|
|
2226
|
+
}
|
|
2227
|
+
},
|
|
2228
|
+
onLostPointerCapture: (event) => {
|
|
2229
|
+
if (press.current?.pointerId === event.pointerId) cancel();
|
|
2230
|
+
},
|
|
2231
|
+
onPointerCancel: (event) => {
|
|
2232
|
+
if (press.current?.pointerId === event.pointerId) cancel();
|
|
2233
|
+
},
|
|
2234
|
+
onPointerDown: (event) => {
|
|
2235
|
+
suppressClick.current = false;
|
|
2236
|
+
if (event.button !== 0 || event.isPrimary === false) return;
|
|
2237
|
+
start("pointer", event.timeStamp, event.pointerId);
|
|
2238
|
+
if (press.current?.pointerId === event.pointerId) event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
2239
|
+
},
|
|
2240
|
+
onPointerUp: (event) => {
|
|
2241
|
+
if (press.current?.source === "pointer" && press.current.pointerId === event.pointerId) end(event.timeStamp);
|
|
2242
|
+
},
|
|
2243
|
+
ref: setButton,
|
|
2244
|
+
title: state === "microphone" ? texts.microphoneHint : label,
|
|
2245
|
+
type: "button",
|
|
2246
|
+
children: [
|
|
2247
|
+
/* @__PURE__ */ jsx16("span", { "aria-hidden": "true", className: "qsl:primary-icon", "data-active": state === "microphone" || state === "recording", children: /* @__PURE__ */ jsx16(MicIcon, {}) }),
|
|
2248
|
+
/* @__PURE__ */ jsx16("span", { "aria-hidden": "true", className: "qsl:primary-icon", "data-active": state === "send", children: /* @__PURE__ */ jsx16(SendIcon, {}) }),
|
|
2249
|
+
/* @__PURE__ */ jsx16("span", { "aria-hidden": "true", className: "qsl:primary-icon", "data-active": state === "stop", children: /* @__PURE__ */ jsx16(SquareIcon2, { fill: "currentColor" }) }),
|
|
2250
|
+
/* @__PURE__ */ jsx16("span", { "aria-hidden": "true", className: "qsl:primary-icon", "data-active": busy, children: /* @__PURE__ */ jsx16(LoaderCircleIcon, { className: "qsl:motion-safe:animate-spin" }) }),
|
|
2251
|
+
state === "recording" && /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", className: "qsl:recording-dot" })
|
|
2252
|
+
]
|
|
2253
|
+
}
|
|
2254
|
+
)
|
|
2255
|
+
] });
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
// packages/quassel/src/ChatInputToolbar.tsx
|
|
2259
|
+
import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2043
2260
|
var COMPACT_WIDTH_PX = 480;
|
|
2044
2261
|
var errorClasses = "qsl:px-3 qsl:py-2 qsl:text-small qsl:text-destructive";
|
|
2045
2262
|
var attachmentItemClasses = "qsl:relative qsl:flex qsl:w-[190px] qsl:max-w-full qsl:min-w-0 qsl:flex-col qsl:overflow-hidden qsl:rounded-lg qsl:border qsl:border-border qsl:text-small";
|
|
@@ -2059,6 +2276,8 @@ function ChatInputToolbar({
|
|
|
2059
2276
|
actions,
|
|
2060
2277
|
toolbarLeft,
|
|
2061
2278
|
toolbarRight,
|
|
2279
|
+
microphone,
|
|
2280
|
+
replaceInput,
|
|
2062
2281
|
texts,
|
|
2063
2282
|
handleRef,
|
|
2064
2283
|
attachments: attachmentsEnabled = true,
|
|
@@ -2074,21 +2293,21 @@ function ChatInputToolbar({
|
|
|
2074
2293
|
const sendScope = useContext4(ChatSendContext);
|
|
2075
2294
|
const allTexts = { ...defaultTexts, ...texts };
|
|
2076
2295
|
const { Button: Button2, StopButton: StopButton2, Toggle: Toggle2 } = useQuasselComponents();
|
|
2077
|
-
const [draft, setDraft] =
|
|
2078
|
-
const [sending, setSending] =
|
|
2079
|
-
const [sendError, setSendError] =
|
|
2080
|
-
const [failedSubmissions, setFailedSubmissions] =
|
|
2081
|
-
const nextFailedId =
|
|
2082
|
-
const pendingSend =
|
|
2083
|
-
const generation =
|
|
2084
|
-
|
|
2296
|
+
const [draft, setDraft] = useState8(initialValue);
|
|
2297
|
+
const [sending, setSending] = useState8(false);
|
|
2298
|
+
const [sendError, setSendError] = useState8();
|
|
2299
|
+
const [failedSubmissions, setFailedSubmissions] = useState8([]);
|
|
2300
|
+
const nextFailedId = useRef8(0);
|
|
2301
|
+
const pendingSend = useRef8(false);
|
|
2302
|
+
const generation = useRef8(0);
|
|
2303
|
+
useLayoutEffect5(() => () => {
|
|
2085
2304
|
generation.current += 1;
|
|
2086
2305
|
}, []);
|
|
2087
|
-
const draftRevision =
|
|
2088
|
-
const [attachments, setAttachments] =
|
|
2089
|
-
const attachmentsRef =
|
|
2090
|
-
const nextAttachmentId =
|
|
2091
|
-
const [attachmentError, setAttachmentError] =
|
|
2306
|
+
const draftRevision = useRef8(0);
|
|
2307
|
+
const [attachments, setAttachments] = useState8([]);
|
|
2308
|
+
const attachmentsRef = useRef8([]);
|
|
2309
|
+
const nextAttachmentId = useRef8(0);
|
|
2310
|
+
const [attachmentError, setAttachmentError] = useState8();
|
|
2092
2311
|
const updateAttachments = (update) => {
|
|
2093
2312
|
attachmentsRef.current = update(attachmentsRef.current);
|
|
2094
2313
|
setAttachments(attachmentsRef.current);
|
|
@@ -2119,10 +2338,11 @@ function ChatInputToolbar({
|
|
|
2119
2338
|
);
|
|
2120
2339
|
}
|
|
2121
2340
|
};
|
|
2122
|
-
const rootRef =
|
|
2123
|
-
const textareaRef =
|
|
2124
|
-
const
|
|
2125
|
-
|
|
2341
|
+
const rootRef = useRef8(null);
|
|
2342
|
+
const textareaRef = useRef8(null);
|
|
2343
|
+
const composing = useRef8(false);
|
|
2344
|
+
const autoFocusSettled = useRef8(false);
|
|
2345
|
+
useEffect6(() => {
|
|
2126
2346
|
if (!autoFocus || autoFocusSettled.current) return;
|
|
2127
2347
|
const input2 = textareaRef.current;
|
|
2128
2348
|
if (!input2) return;
|
|
@@ -2167,8 +2387,8 @@ function ChatInputToolbar({
|
|
|
2167
2387
|
window.removeEventListener("blur", settle);
|
|
2168
2388
|
};
|
|
2169
2389
|
}, [autoFocus, disabled, onAutoFocusSettled]);
|
|
2170
|
-
const [compact, setCompact] =
|
|
2171
|
-
const pendingFocus =
|
|
2390
|
+
const [compact, setCompact] = useState8(false);
|
|
2391
|
+
const pendingFocus = useRef8(false);
|
|
2172
2392
|
const fileInput = useFileInput({
|
|
2173
2393
|
disabled: disabled || sending,
|
|
2174
2394
|
onFiles: addFiles,
|
|
@@ -2184,17 +2404,21 @@ function ChatInputToolbar({
|
|
|
2184
2404
|
});
|
|
2185
2405
|
const toolbar = layout === "toolbar";
|
|
2186
2406
|
const inline = layout === "inline";
|
|
2187
|
-
const
|
|
2188
|
-
const
|
|
2407
|
+
const pill = layout === "pill";
|
|
2408
|
+
const inputRows = inline || toolbar || pill ? 1 : rows;
|
|
2409
|
+
const inputMaxRows = toolbar ? 1 : pill ? maxRows ?? 6 : inline ? maxRows ?? 1 : maxRows;
|
|
2410
|
+
const microphoneBusy = pill && !!microphone && microphone.state !== "idle";
|
|
2411
|
+
const replacingInput = pill && replaceInput !== void 0 && replaceInput !== null && typeof replaceInput !== "boolean";
|
|
2189
2412
|
const rootClasses = cn(
|
|
2190
|
-
"qsl:flex qsl:shrink-0 qsl:flex-col
|
|
2191
|
-
"qsl:in-data-[tone=material]:rounded-xl qsl:in-data-[tone=material]:border-glass-edge/50 qsl:in-data-[tone=material]:bg-background",
|
|
2413
|
+
"qsl:flex qsl:shrink-0 qsl:flex-col",
|
|
2414
|
+
pill ? "qsl:min-w-0" : "qsl:overflow-hidden qsl:rounded-[var(--qsl-input-card-radius,--theme(--radius-xl))] qsl:border qsl:border-border-strong qsl:bg-background qsl:shadow-bar qsl:transition-colors qsl:focus-within:border-primary qsl:in-data-[tone=material]:rounded-xl qsl:in-data-[tone=material]:border-glass-edge/50 qsl:in-data-[tone=material]:bg-background",
|
|
2192
2415
|
toolbar && "qsl:h-full qsl:min-w-0 qsl:flex-row qsl:items-center qsl:rounded-lg qsl:data-[working=true]:animate-working-pulse qsl:data-[working=true]:border-primary qsl:motion-reduce:data-[working=true]:animate-none",
|
|
2193
2416
|
fileInput.dragging && "qsl:outline-2 qsl:outline-offset-[3px] qsl:outline-dashed qsl:outline-primary"
|
|
2194
2417
|
);
|
|
2195
2418
|
const textareaClasses = cn(
|
|
2196
|
-
"qsl:resize-none qsl:overscroll-contain qsl:border-0 qsl:bg-transparent qsl:text-
|
|
2197
|
-
|
|
2419
|
+
"qsl:resize-none qsl:overscroll-contain qsl:border-0 qsl:bg-transparent qsl:text-foreground qsl:placeholder:text-muted-foreground",
|
|
2420
|
+
!pill && "qsl:text-body qsl:focus:outline-none",
|
|
2421
|
+
pill ? "qsl:pill-input" : inline ? "qsl:min-w-0 qsl:flex-1 qsl:px-2.5 qsl:py-2" : toolbar ? "qsl:w-full qsl:min-w-0 qsl:flex-auto qsl:px-2 qsl:py-[3px]" : "qsl:px-3 qsl:pt-3 qsl:pb-2"
|
|
2198
2422
|
);
|
|
2199
2423
|
const toolbarRowClasses = cn(
|
|
2200
2424
|
"qsl:flex qsl:items-center qsl:gap-2 qsl:border-t qsl:border-border-soft qsl:px-2 qsl:py-1.5 qsl:in-data-[tone=overseer-details]:flex-nowrap",
|
|
@@ -2229,10 +2453,10 @@ function ChatInputToolbar({
|
|
|
2229
2453
|
fileInput.clearDragging();
|
|
2230
2454
|
}
|
|
2231
2455
|
}));
|
|
2232
|
-
|
|
2456
|
+
useEffect6(() => {
|
|
2233
2457
|
onDraftChange?.(draft);
|
|
2234
2458
|
}, [draft, onDraftChange]);
|
|
2235
|
-
|
|
2459
|
+
useEffect6(() => {
|
|
2236
2460
|
if (!pendingFocus.current) {
|
|
2237
2461
|
return;
|
|
2238
2462
|
}
|
|
@@ -2257,10 +2481,10 @@ function ChatInputToolbar({
|
|
|
2257
2481
|
element.style.height = `${Math.min(contentHeight, maximum)}px`;
|
|
2258
2482
|
element.style.overflowY = contentHeight > maximum ? "auto" : "hidden";
|
|
2259
2483
|
}, [inputMaxRows, inputRows]);
|
|
2260
|
-
|
|
2484
|
+
useEffect6(() => {
|
|
2261
2485
|
applyAutoGrow();
|
|
2262
|
-
}, [draft, applyAutoGrow]);
|
|
2263
|
-
|
|
2486
|
+
}, [draft, replacingInput, applyAutoGrow]);
|
|
2487
|
+
useEffect6(() => {
|
|
2264
2488
|
const element = textareaRef.current;
|
|
2265
2489
|
if (!element || inputMaxRows === void 0) {
|
|
2266
2490
|
return;
|
|
@@ -2274,8 +2498,8 @@ function ChatInputToolbar({
|
|
|
2274
2498
|
});
|
|
2275
2499
|
observer.observe(element);
|
|
2276
2500
|
return () => observer.disconnect();
|
|
2277
|
-
}, [inputMaxRows, applyAutoGrow]);
|
|
2278
|
-
|
|
2501
|
+
}, [inputMaxRows, replacingInput, applyAutoGrow]);
|
|
2502
|
+
useEffect6(() => {
|
|
2279
2503
|
const element = rootRef.current;
|
|
2280
2504
|
if (!element) {
|
|
2281
2505
|
return;
|
|
@@ -2287,7 +2511,7 @@ function ChatInputToolbar({
|
|
|
2287
2511
|
return () => observer.disconnect();
|
|
2288
2512
|
}, []);
|
|
2289
2513
|
const hasAttachments = attachments.length > 0;
|
|
2290
|
-
|
|
2514
|
+
useEffect6(() => {
|
|
2291
2515
|
onAttachmentsChange?.(hasAttachments);
|
|
2292
2516
|
}, [hasAttachments, onAttachmentsChange]);
|
|
2293
2517
|
const capabilityError = hasAttachments && attachmentCapabilitiesError ? attachmentCapabilitiesError : attachmentCapabilities ? attachmentCapabilityError(attachments.map((entry) => ({
|
|
@@ -2298,13 +2522,13 @@ function ChatInputToolbar({
|
|
|
2298
2522
|
const preparing = attachments.some((entry) => entry.status === "reading");
|
|
2299
2523
|
const attachmentBlocked = attachments.some((entry) => entry.status !== "ready");
|
|
2300
2524
|
const currentError = sendError ?? attachmentError ?? capabilityError ?? attachments.find((entry) => entry.status === "error")?.error;
|
|
2301
|
-
|
|
2525
|
+
useEffect6(() => {
|
|
2302
2526
|
onErrorChange?.(currentError);
|
|
2303
2527
|
}, [currentError, onErrorChange]);
|
|
2304
2528
|
const send = async (onSubmit = onSend, allowEmpty = false) => {
|
|
2305
2529
|
const text = draft.trim();
|
|
2306
2530
|
const snapshot = attachmentsRef.current;
|
|
2307
|
-
if (!allowEmpty && !text && !snapshot.length || disabled || sendDisabled || pendingSend.current || capabilityError || snapshot.some((entry) => entry.status !== "ready")) {
|
|
2531
|
+
if (!allowEmpty && !text && !snapshot.length || disabled || sendDisabled || microphoneBusy || replacingInput || pendingSend.current || capabilityError || snapshot.some((entry) => entry.status !== "ready")) {
|
|
2308
2532
|
return;
|
|
2309
2533
|
}
|
|
2310
2534
|
pendingSend.current = true;
|
|
@@ -2349,21 +2573,21 @@ function ChatInputToolbar({
|
|
|
2349
2573
|
}
|
|
2350
2574
|
};
|
|
2351
2575
|
const onKeyDown = (event) => {
|
|
2352
|
-
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing && event.nativeEvent.keyCode !== 229 && (sendShortcut === "enter" || event.metaKey || event.ctrlKey)) {
|
|
2576
|
+
if (event.key === "Enter" && !event.shiftKey && !composing.current && !event.nativeEvent.isComposing && event.nativeEvent.keyCode !== 229 && (sendShortcut === "enter" || event.metaKey || event.ctrlKey)) {
|
|
2353
2577
|
event.preventDefault();
|
|
2354
2578
|
if (!event.repeat) void send();
|
|
2355
2579
|
}
|
|
2356
2580
|
};
|
|
2357
|
-
const attachmentPreviews = attachments.length > 0 && /* @__PURE__ */
|
|
2358
|
-
entry.status === "ready" && entry.input.mediaType.startsWith("image/") && /* @__PURE__ */
|
|
2359
|
-
entry.status === "ready" && entry.input.mediaType.startsWith("video/") && /* @__PURE__ */
|
|
2360
|
-
/* @__PURE__ */
|
|
2361
|
-
/* @__PURE__ */
|
|
2362
|
-
/* @__PURE__ */
|
|
2363
|
-
entry.status === "reading" && /* @__PURE__ */
|
|
2364
|
-
entry.status === "error" && /* @__PURE__ */
|
|
2581
|
+
const attachmentPreviews = attachments.length > 0 && /* @__PURE__ */ jsx17("ul", { className: "qsl:flex qsl:max-h-[240px] qsl:flex-wrap qsl:gap-2 qsl:overflow-y-auto qsl:overscroll-contain qsl:px-3 qsl:pt-2.5 qsl:in-data-[tone=overseer-details]:max-h-40 qsl:in-data-[tone=overseer-details]:overflow-y-auto", "aria-label": allTexts.attachments, children: attachments.map((entry) => /* @__PURE__ */ jsxs9("li", { className: attachmentItemClasses, children: [
|
|
2582
|
+
entry.status === "ready" && entry.input.mediaType.startsWith("image/") && /* @__PURE__ */ jsx17("img", { className: attachmentMediaClasses, src: `data:${entry.input.mediaType};base64,${entry.input.data}`, alt: entry.file.name }),
|
|
2583
|
+
entry.status === "ready" && entry.input.mediaType.startsWith("video/") && /* @__PURE__ */ jsx17("video", { className: attachmentMediaClasses, controls: true, preload: "metadata", "aria-label": entry.file.name, src: `data:${entry.input.mediaType};base64,${entry.input.data}` }),
|
|
2584
|
+
/* @__PURE__ */ jsxs9("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:pr-8 qsl:[overflow-wrap:anywhere]", children: [
|
|
2585
|
+
/* @__PURE__ */ jsx17("span", { className: "qsl:font-medium", title: entry.file.name, children: entry.file.name }),
|
|
2586
|
+
/* @__PURE__ */ jsx17("small", { children: formatAttachmentSize(entry.file.size) }),
|
|
2587
|
+
entry.status === "reading" && /* @__PURE__ */ jsx17("small", { role: "status", children: allTexts.attachmentPreparing }),
|
|
2588
|
+
entry.status === "error" && /* @__PURE__ */ jsx17("small", { role: "alert", children: entry.error })
|
|
2365
2589
|
] }),
|
|
2366
|
-
/* @__PURE__ */
|
|
2590
|
+
/* @__PURE__ */ jsx17(
|
|
2367
2591
|
Button2,
|
|
2368
2592
|
{
|
|
2369
2593
|
"aria-label": fillText(allTexts.removeAttachment, { name: entry.file.name }),
|
|
@@ -2377,16 +2601,16 @@ function ChatInputToolbar({
|
|
|
2377
2601
|
size: "icon-sm",
|
|
2378
2602
|
title: fillText(allTexts.removeAttachment, { name: entry.file.name }),
|
|
2379
2603
|
variant: "outline",
|
|
2380
|
-
children: /* @__PURE__ */
|
|
2604
|
+
children: /* @__PURE__ */ jsx17(XIcon3, {})
|
|
2381
2605
|
}
|
|
2382
2606
|
)
|
|
2383
2607
|
] }, entry.id)) });
|
|
2384
|
-
const errors = /* @__PURE__ */
|
|
2385
|
-
capabilityError && /* @__PURE__ */
|
|
2386
|
-
attachmentError && /* @__PURE__ */
|
|
2387
|
-
sendError && /* @__PURE__ */
|
|
2388
|
-
failedSubmissions.map((failed) => /* @__PURE__ */
|
|
2389
|
-
/* @__PURE__ */
|
|
2608
|
+
const errors = /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2609
|
+
capabilityError && /* @__PURE__ */ jsx17("p", { className: errorClasses, role: "alert", children: capabilityError }),
|
|
2610
|
+
attachmentError && /* @__PURE__ */ jsx17("p", { className: errorClasses, role: "alert", children: attachmentError }),
|
|
2611
|
+
sendError && /* @__PURE__ */ jsx17("p", { className: errorClasses, role: "alert", children: sendError }),
|
|
2612
|
+
failedSubmissions.map((failed) => /* @__PURE__ */ jsxs9("div", { className: cn(errorClasses, "qsl:flex qsl:flex-wrap qsl:items-center qsl:gap-2"), role: "status", children: [
|
|
2613
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
2390
2614
|
allTexts.notSent,
|
|
2391
2615
|
" ",
|
|
2392
2616
|
failed.draft.slice(0, 100),
|
|
@@ -2394,7 +2618,7 @@ function ChatInputToolbar({
|
|
|
2394
2618
|
failed.attachments.length > 0 ? ` ${fillText(allTexts.notSentAttachments, { count: failed.attachments.length })}` : "",
|
|
2395
2619
|
" "
|
|
2396
2620
|
] }),
|
|
2397
|
-
/* @__PURE__ */
|
|
2621
|
+
/* @__PURE__ */ jsx17(Button2, { disabled: disabled || sending, size: "sm", variant: "outline", onClick: () => {
|
|
2398
2622
|
try {
|
|
2399
2623
|
validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file), allTexts);
|
|
2400
2624
|
} catch (error) {
|
|
@@ -2410,9 +2634,9 @@ function ChatInputToolbar({
|
|
|
2410
2634
|
}, children: allTexts.insertUnsent })
|
|
2411
2635
|
] }, failed.id))
|
|
2412
2636
|
] });
|
|
2413
|
-
const sendButtons = /* @__PURE__ */
|
|
2414
|
-
running2 && onStop && !hasContent && /* @__PURE__ */
|
|
2415
|
-
(!running2 || !onStop || hasContent) && /* @__PURE__ */
|
|
2637
|
+
const sendButtons = /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2638
|
+
running2 && onStop && !hasContent && /* @__PURE__ */ jsx17(StopButton2, { className: "qsl:flex-none", label: allTexts.stop, onClick: onStop, size: "icon-sm" }),
|
|
2639
|
+
(!running2 || !onStop || hasContent) && /* @__PURE__ */ jsx17(
|
|
2416
2640
|
Button2,
|
|
2417
2641
|
{
|
|
2418
2642
|
"aria-label": allTexts.send,
|
|
@@ -2423,53 +2647,62 @@ function ChatInputToolbar({
|
|
|
2423
2647
|
},
|
|
2424
2648
|
size: "icon-sm",
|
|
2425
2649
|
title: running2 ? allTexts.sendIntoRun : allTexts.send,
|
|
2426
|
-
children: /* @__PURE__ */
|
|
2650
|
+
children: /* @__PURE__ */ jsx17(SendIcon2, { size: 14 })
|
|
2427
2651
|
}
|
|
2428
2652
|
)
|
|
2429
2653
|
] });
|
|
2430
|
-
const
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2654
|
+
const actionButtons = actions?.map((action, index) => action.active === void 0 ? /* @__PURE__ */ jsxs9(Button2, { "aria-label": action.label ?? action.title, disabled: action.disabled, onClick: action.onClick, size: "sm", title: action.title ?? action.label, variant: "ghost", children: [
|
|
2655
|
+
action.icon,
|
|
2656
|
+
action.label && /* @__PURE__ */ jsx17("span", { className: action.icon ? "qsl:in-data-[compact=true]:hidden" : void 0, children: action.label })
|
|
2657
|
+
] }, index) : /* @__PURE__ */ jsxs9(Toggle2, { "aria-label": action.label ?? action.title, disabled: action.disabled, onPressedChange: () => action.onClick(), pressed: action.active, size: "sm", title: action.title ?? action.label, children: [
|
|
2658
|
+
action.icon,
|
|
2659
|
+
action.label && /* @__PURE__ */ jsx17("span", { className: action.icon ? "qsl:in-data-[compact=true]:hidden" : void 0, children: action.label })
|
|
2660
|
+
] }, index));
|
|
2661
|
+
const attachButton = attachmentsEnabled && /* @__PURE__ */ jsx17(
|
|
2662
|
+
Button2,
|
|
2663
|
+
{
|
|
2664
|
+
"aria-label": allTexts.attachFiles,
|
|
2665
|
+
disabled: disabled || sending,
|
|
2666
|
+
onClick: () => fileInput.inputRef.current?.click(),
|
|
2667
|
+
size: "icon-sm",
|
|
2668
|
+
title: fillText(allTexts.attachFilesHint, { count: MAX_CHAT_ATTACHMENTS, size: maxAttachmentSizeLabel }),
|
|
2669
|
+
variant: "ghost",
|
|
2670
|
+
children: /* @__PURE__ */ jsx17(PaperclipIcon, {})
|
|
2671
|
+
}
|
|
2672
|
+
);
|
|
2673
|
+
const detailToolbar = /* @__PURE__ */ jsxs9("div", { className: toolbarRowClasses, "data-input": "controls", children: [
|
|
2674
|
+
/* @__PURE__ */ jsxs9("div", { className: cn(toolbarSideClasses, "qsl:in-data-[tone=overseer-details]:flex-none", inline || toolbar ? "qsl:flex-none" : "qsl:flex-auto"), children: [
|
|
2675
|
+
attachButton,
|
|
2676
|
+
preparing && /* @__PURE__ */ jsx17("span", { className: cn("qsl:text-small qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children: allTexts.attachmentsPreparing }),
|
|
2677
|
+
actionButtons,
|
|
2452
2678
|
toolbarLeft
|
|
2453
2679
|
] }),
|
|
2454
|
-
/* @__PURE__ */
|
|
2680
|
+
/* @__PURE__ */ jsxs9("div", { className: cn(toolbarSideClasses, "qsl:in-data-[tone=overseer-details]:flex-1", inline || toolbar ? "qsl:flex-none" : "qsl:flex-[0_1_auto]"), children: [
|
|
2455
2681
|
toolbarRight,
|
|
2456
2682
|
!toolbar && sendButtons
|
|
2457
2683
|
] })
|
|
2458
2684
|
] });
|
|
2459
|
-
const details = /* @__PURE__ */
|
|
2685
|
+
const details = /* @__PURE__ */ jsxs9("div", { className: "qsl:min-w-0", "data-compact": compact, "data-quassel": "", children: [
|
|
2460
2686
|
attachmentPreviews,
|
|
2461
2687
|
errors,
|
|
2462
2688
|
detailToolbar
|
|
2463
2689
|
] });
|
|
2464
|
-
const input = /* @__PURE__ */
|
|
2690
|
+
const input = /* @__PURE__ */ jsx17(
|
|
2465
2691
|
"textarea",
|
|
2466
2692
|
{
|
|
2467
2693
|
"aria-controls": inputAriaControls,
|
|
2468
2694
|
className: textareaClasses,
|
|
2469
2695
|
"aria-label": allTexts.placeholder,
|
|
2470
|
-
disabled: !toolbar && disabled,
|
|
2696
|
+
disabled: !toolbar && (disabled || microphoneBusy),
|
|
2697
|
+
hidden: replacingInput,
|
|
2471
2698
|
readOnly: toolbar && disabled,
|
|
2472
2699
|
onChange: (event) => changeDraft(event.target.value),
|
|
2700
|
+
onCompositionStart: () => {
|
|
2701
|
+
composing.current = true;
|
|
2702
|
+
},
|
|
2703
|
+
onCompositionEnd: () => {
|
|
2704
|
+
composing.current = false;
|
|
2705
|
+
},
|
|
2473
2706
|
onKeyDown,
|
|
2474
2707
|
placeholder: running2 ? allTexts.steeringPlaceholder : allTexts.placeholder,
|
|
2475
2708
|
ref: textareaRef,
|
|
@@ -2477,7 +2710,7 @@ function ChatInputToolbar({
|
|
|
2477
2710
|
value: draft
|
|
2478
2711
|
}
|
|
2479
2712
|
);
|
|
2480
|
-
return /* @__PURE__ */
|
|
2713
|
+
return /* @__PURE__ */ jsxs9(
|
|
2481
2714
|
"div",
|
|
2482
2715
|
{
|
|
2483
2716
|
className: rootClasses,
|
|
@@ -2488,7 +2721,7 @@ function ChatInputToolbar({
|
|
|
2488
2721
|
ref: rootRef,
|
|
2489
2722
|
...attachmentsEnabled ? fileInput.dropProps : {},
|
|
2490
2723
|
children: [
|
|
2491
|
-
attachmentsEnabled && /* @__PURE__ */
|
|
2724
|
+
attachmentsEnabled && /* @__PURE__ */ jsx17(
|
|
2492
2725
|
"input",
|
|
2493
2726
|
{
|
|
2494
2727
|
"aria-label": allTexts.attachFiles,
|
|
@@ -2499,17 +2732,48 @@ function ChatInputToolbar({
|
|
|
2499
2732
|
}
|
|
2500
2733
|
),
|
|
2501
2734
|
!toolbar && attachmentPreviews,
|
|
2502
|
-
|
|
2735
|
+
pill ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2503
2736
|
errors,
|
|
2504
|
-
/* @__PURE__ */
|
|
2737
|
+
preparing && /* @__PURE__ */ jsx17("span", { className: "qsl:sr-only", role: "status", children: allTexts.attachmentsPreparing }),
|
|
2738
|
+
/* @__PURE__ */ jsxs9("div", { className: "qsl:pill-row", "data-input": "pill", children: [
|
|
2739
|
+
(actionButtons?.length || toolbarLeft) && /* @__PURE__ */ jsxs9("div", { className: "qsl:pill-slot", "data-input": "left", children: [
|
|
2740
|
+
actionButtons,
|
|
2741
|
+
toolbarLeft
|
|
2742
|
+
] }),
|
|
2743
|
+
input,
|
|
2744
|
+
replacingInput && /* @__PURE__ */ jsx17("div", { className: "qsl:pill-replacement", "data-input": "replacement", children: replaceInput }),
|
|
2745
|
+
(toolbarRight || attachmentsEnabled) && /* @__PURE__ */ jsxs9("div", { className: "qsl:pill-slot", "data-input": "right", children: [
|
|
2746
|
+
toolbarRight,
|
|
2747
|
+
attachButton
|
|
2748
|
+
] }),
|
|
2749
|
+
/* @__PURE__ */ jsx17(
|
|
2750
|
+
ComposerPrimaryAction,
|
|
2751
|
+
{
|
|
2752
|
+
disabled,
|
|
2753
|
+
hasContent,
|
|
2754
|
+
microphone,
|
|
2755
|
+
onSend: () => {
|
|
2756
|
+
void send();
|
|
2757
|
+
},
|
|
2758
|
+
onStop,
|
|
2759
|
+
running: running2,
|
|
2760
|
+
sendDisabled: sendDisabled || attachmentBlocked || !!capabilityError || replacingInput,
|
|
2761
|
+
sending,
|
|
2762
|
+
texts: allTexts
|
|
2763
|
+
}
|
|
2764
|
+
)
|
|
2765
|
+
] })
|
|
2766
|
+
] }) : inline ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2767
|
+
errors,
|
|
2768
|
+
/* @__PURE__ */ jsxs9("div", { className: "qsl:flex qsl:min-w-0 qsl:items-end", children: [
|
|
2505
2769
|
input,
|
|
2506
2770
|
detailToolbar
|
|
2507
2771
|
] })
|
|
2508
2772
|
] }) : input,
|
|
2509
|
-
toolbar ? /* @__PURE__ */
|
|
2510
|
-
/* @__PURE__ */
|
|
2511
|
-
detailsContainer &&
|
|
2512
|
-
] }) : !inline && /* @__PURE__ */
|
|
2773
|
+
toolbar ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2774
|
+
/* @__PURE__ */ jsx17("div", { className: "qsl:flex qsl:flex-none qsl:items-center qsl:gap-1 qsl:p-1", children: sendButtons }),
|
|
2775
|
+
detailsContainer && createPortal3(details, detailsContainer)
|
|
2776
|
+
] }) : !inline && !pill && /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
2513
2777
|
errors,
|
|
2514
2778
|
detailToolbar
|
|
2515
2779
|
] })
|
|
@@ -2520,7 +2784,7 @@ function ChatInputToolbar({
|
|
|
2520
2784
|
|
|
2521
2785
|
// packages/quassel/src/DetailModeSwitch.tsx
|
|
2522
2786
|
import { CircleDotIcon, EyeOffIcon, ListCollapseIcon, ListIcon, ShapesIcon, TagsIcon, TextAlignJustifyIcon } from "lucide-react";
|
|
2523
|
-
import { jsx as
|
|
2787
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
2524
2788
|
var DETAIL_MODES = ["off", "current", "icons", "chips", "grouped", "compact", "full"];
|
|
2525
2789
|
var TEXT_KEYS = {
|
|
2526
2790
|
off: "detailModeOff",
|
|
@@ -2554,18 +2818,18 @@ function DetailModeSwitch({
|
|
|
2554
2818
|
const next2 = modes[(current < 0 ? 0 : current + 1) % modes.length];
|
|
2555
2819
|
const Icon = ICONS[mode];
|
|
2556
2820
|
const title = `${allTexts.detailModeTitle}: ${detailModeLabel(mode, texts)}`;
|
|
2557
|
-
return /* @__PURE__ */
|
|
2821
|
+
return /* @__PURE__ */ jsx18(Button2, { "aria-label": title, className, onClick: () => onChange(next2), size: "icon-sm", title, variant: "ghost", children: /* @__PURE__ */ jsx18(Icon, {}) });
|
|
2558
2822
|
}
|
|
2559
2823
|
|
|
2560
2824
|
// packages/quassel/src/TranscriptModeSwitch.tsx
|
|
2561
2825
|
import { MessagesSquareIcon, MessageSquareIcon } from "lucide-react";
|
|
2562
|
-
import { jsx as
|
|
2826
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
2563
2827
|
function TranscriptModeSwitch({ mode, onChange, texts, className }) {
|
|
2564
2828
|
const labels = { ...defaultTexts, ...texts };
|
|
2565
2829
|
const { Button: Button2 } = useQuasselComponents();
|
|
2566
2830
|
const latest = mode === "latest";
|
|
2567
2831
|
const title = latest ? labels.showAllMessages : labels.showLatestExchange;
|
|
2568
|
-
return /* @__PURE__ */
|
|
2832
|
+
return /* @__PURE__ */ jsx19(
|
|
2569
2833
|
Button2,
|
|
2570
2834
|
{
|
|
2571
2835
|
"aria-label": title,
|
|
@@ -2575,19 +2839,19 @@ function TranscriptModeSwitch({ mode, onChange, texts, className }) {
|
|
|
2575
2839
|
size: "icon-sm",
|
|
2576
2840
|
title,
|
|
2577
2841
|
variant: "ghost",
|
|
2578
|
-
children: latest ? /* @__PURE__ */
|
|
2842
|
+
children: latest ? /* @__PURE__ */ jsx19(MessageSquareIcon, {}) : /* @__PURE__ */ jsx19(MessagesSquareIcon, {})
|
|
2579
2843
|
}
|
|
2580
2844
|
);
|
|
2581
2845
|
}
|
|
2582
2846
|
|
|
2583
2847
|
// packages/quassel/src/TimestampSwitch.tsx
|
|
2584
2848
|
import { ClockFadingIcon, ClockIcon } from "lucide-react";
|
|
2585
|
-
import { jsx as
|
|
2849
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2586
2850
|
function TimestampSwitch({ showTimestamps, onChange, texts, className }) {
|
|
2587
2851
|
const labels = { ...defaultTexts, ...texts };
|
|
2588
2852
|
const { Button: Button2 } = useQuasselComponents();
|
|
2589
2853
|
const title = showTimestamps ? labels.hideTimestamps : labels.showTimestamps;
|
|
2590
|
-
return /* @__PURE__ */
|
|
2854
|
+
return /* @__PURE__ */ jsx20(
|
|
2591
2855
|
Button2,
|
|
2592
2856
|
{
|
|
2593
2857
|
"aria-label": title,
|
|
@@ -2597,7 +2861,7 @@ function TimestampSwitch({ showTimestamps, onChange, texts, className }) {
|
|
|
2597
2861
|
size: "icon-sm",
|
|
2598
2862
|
title,
|
|
2599
2863
|
variant: "ghost",
|
|
2600
|
-
children: showTimestamps ? /* @__PURE__ */
|
|
2864
|
+
children: showTimestamps ? /* @__PURE__ */ jsx20(ClockIcon, {}) : /* @__PURE__ */ jsx20(ClockFadingIcon, {})
|
|
2601
2865
|
}
|
|
2602
2866
|
);
|
|
2603
2867
|
}
|