@green-on-grey/ui-kit 0.1.1 → 0.1.4
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/dist/chat.js +21 -21
- package/dist/chat.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/package.json +1 -1
package/dist/chat.js
CHANGED
|
@@ -180,27 +180,29 @@ function b() {
|
|
|
180
180
|
//#endregion
|
|
181
181
|
//#region src/chat/Chat.tsx
|
|
182
182
|
function x(n) {
|
|
183
|
-
let r = i(), a = e(), [l, u] = p(""), [m, h] = p([]), [g, _] = p(!1), [v, x] = p(null), S = f(null), C = f(null), w = b(), { messages: T, busy: E = !1 } = n, D = T[0]?.id, O = T[T.length - 1], k = O?.text.length,
|
|
183
|
+
let r = i(), a = e(), [l, u] = p(""), [m, h] = p([]), [g, _] = p(!1), [v, x] = p(null), S = f(null), C = f(null), w = b(), { messages: T, busy: E = !1 } = n, D = T[0]?.id, O = T[T.length - 1], k = O?.id, A = O?.text.length, j = O?.steps?.length, M = f({ height: 0 });
|
|
184
184
|
d(() => {
|
|
185
185
|
let e = S.current;
|
|
186
186
|
if (!e) return;
|
|
187
|
-
let t =
|
|
188
|
-
e.scrollTop = t.first !== void 0 && D !== t.first &&
|
|
187
|
+
let t = M.current;
|
|
188
|
+
e.scrollTop = t.first !== void 0 && D !== t.first && k === t.last ? e.scrollTop + (e.scrollHeight - t.height) : e.scrollHeight, M.current = {
|
|
189
189
|
first: D,
|
|
190
|
+
last: k,
|
|
190
191
|
height: e.scrollHeight
|
|
191
192
|
};
|
|
192
193
|
}, [
|
|
193
194
|
T.length,
|
|
194
195
|
D,
|
|
195
196
|
k,
|
|
196
|
-
A
|
|
197
|
+
A,
|
|
198
|
+
j
|
|
197
199
|
]);
|
|
198
|
-
let
|
|
200
|
+
let N = async (e, t) => {
|
|
199
201
|
let r = e.trim();
|
|
200
202
|
if (!(!r && m.length === 0 || E || n.readOnly)) try {
|
|
201
203
|
await n.onSend(r, m.map((e) => e.id), t), u(""), h([]), x(null);
|
|
202
204
|
} catch {}
|
|
203
|
-
},
|
|
205
|
+
}, P = async (e) => {
|
|
204
206
|
if (e?.length && n.onUpload) {
|
|
205
207
|
_(!0);
|
|
206
208
|
for (let t of Array.from(e)) {
|
|
@@ -217,7 +219,7 @@ function x(n) {
|
|
|
217
219
|
}
|
|
218
220
|
_(!1), C.current && (C.current.value = "");
|
|
219
221
|
}
|
|
220
|
-
},
|
|
222
|
+
}, F = async () => {
|
|
221
223
|
let e = await w.stop();
|
|
222
224
|
if (!e || !n.onTranscribe) return;
|
|
223
225
|
let t = await n.onTranscribe(e).catch(() => null);
|
|
@@ -225,9 +227,9 @@ function x(n) {
|
|
|
225
227
|
kind: "error",
|
|
226
228
|
title: r("chat.voice.notRecognized")
|
|
227
229
|
}));
|
|
228
|
-
},
|
|
229
|
-
(e.key === " " || e.key === "Enter") && (e.preventDefault(), !e.repeat && (w.recording ?
|
|
230
|
-
},
|
|
230
|
+
}, I = (e) => {
|
|
231
|
+
(e.key === " " || e.key === "Enter") && (e.preventDefault(), !e.repeat && (w.recording ? F() : w.start()));
|
|
232
|
+
}, L = r("chat.placeholder", { name: n.agentName });
|
|
231
233
|
return /* @__PURE__ */ c("section", {
|
|
232
234
|
className: "nb-chat",
|
|
233
235
|
"aria-label": r("chat.title"),
|
|
@@ -283,12 +285,10 @@ function x(n) {
|
|
|
283
285
|
className: "acts",
|
|
284
286
|
children: [/* @__PURE__ */ s("button", {
|
|
285
287
|
className: "btn sm primary",
|
|
286
|
-
disabled: E,
|
|
287
288
|
onClick: () => n.onConfirm?.(e.id, !0),
|
|
288
289
|
children: r("chat.confirm.approve")
|
|
289
290
|
}), /* @__PURE__ */ s("button", {
|
|
290
291
|
className: "btn sm",
|
|
291
|
-
disabled: E,
|
|
292
292
|
onClick: () => n.onConfirm?.(e.id, !1),
|
|
293
293
|
children: r("chat.confirm.reject")
|
|
294
294
|
})]
|
|
@@ -347,7 +347,7 @@ function x(n) {
|
|
|
347
347
|
multiple: !0,
|
|
348
348
|
hidden: !0,
|
|
349
349
|
accept: n.upload?.accept,
|
|
350
|
-
onChange: (e) =>
|
|
350
|
+
onChange: (e) => P(e.target.files)
|
|
351
351
|
})] }),
|
|
352
352
|
w.recording ? /* @__PURE__ */ s("div", {
|
|
353
353
|
className: "rec-box",
|
|
@@ -355,11 +355,11 @@ function x(n) {
|
|
|
355
355
|
}) : /* @__PURE__ */ s("textarea", {
|
|
356
356
|
rows: 1,
|
|
357
357
|
value: l,
|
|
358
|
-
placeholder:
|
|
359
|
-
"aria-label":
|
|
358
|
+
placeholder: L,
|
|
359
|
+
"aria-label": L,
|
|
360
360
|
onChange: (e) => u(e.target.value),
|
|
361
361
|
onKeyDown: (e) => {
|
|
362
|
-
e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing && (e.preventDefault(),
|
|
362
|
+
e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing && (e.preventDefault(), N(l, !1));
|
|
363
363
|
}
|
|
364
364
|
}),
|
|
365
365
|
n.onTranscribe && /* @__PURE__ */ s("button", {
|
|
@@ -370,9 +370,9 @@ function x(n) {
|
|
|
370
370
|
onPointerDown: (e) => {
|
|
371
371
|
e.preventDefault(), w.recording || w.start();
|
|
372
372
|
},
|
|
373
|
-
onPointerUp: () => w.recording &&
|
|
374
|
-
onPointerLeave: () => w.recording &&
|
|
375
|
-
onKeyDown:
|
|
373
|
+
onPointerUp: () => w.recording && F(),
|
|
374
|
+
onPointerLeave: () => w.recording && F(),
|
|
375
|
+
onKeyDown: I,
|
|
376
376
|
children: /* @__PURE__ */ s(t, { name: "mic" })
|
|
377
377
|
}),
|
|
378
378
|
n.onCancel && E ? /* @__PURE__ */ s("button", {
|
|
@@ -384,7 +384,7 @@ function x(n) {
|
|
|
384
384
|
className: "iconbtn fill",
|
|
385
385
|
"aria-label": r("chat.send"),
|
|
386
386
|
disabled: !l.trim() && m.length === 0 || E,
|
|
387
|
-
onClick: () =>
|
|
387
|
+
onClick: () => N(l, !1),
|
|
388
388
|
children: /* @__PURE__ */ s(t, { name: "send" })
|
|
389
389
|
})
|
|
390
390
|
]
|
|
@@ -420,7 +420,7 @@ function x(n) {
|
|
|
420
420
|
}), /* @__PURE__ */ s("button", {
|
|
421
421
|
className: "btn primary sm",
|
|
422
422
|
disabled: !v.trim() || E,
|
|
423
|
-
onClick: () =>
|
|
423
|
+
onClick: () => N(v, !0),
|
|
424
424
|
children: r("chat.send")
|
|
425
425
|
})]
|
|
426
426
|
})
|
package/dist/chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","names":["_Fragment","_Fragment","_Fragment","useToast","Icon","Modal","Markdown","useKitT","UIKIT_NS","intlLocale","lng","markTime","language","iso","visibleSteps","open","steps","STEP","MessageView","useTranslation","setOpen","useState","m","mark","i18n","atts","a","Fragment","t","waiting","v","s","onRetry","busy","useRecorder","supported","recording","setRecording","seconds","setSeconds","error","setError","rec","useRef","chunks","timer","stopped","cleanup","useCallback","tr","start","stream","r","blob","began","stop","resolve","useEffect","Chat","toast","text","setText","pending","setPending","uploading","setUploading","transcript","setTranscript","listRef","fileRef","recorder","props","first","messages","tail","tailText","tailSteps","lastLayout","el","was","send","msg","body","f","voice","attach","files","x","stopAndTranscribe","voiceKey","placeholder","c","y","ConversationList","creating","setCreating","onKey","onClose","item","currentId","onPick","inGroup","conversations","g","topics","archive","onCreate","NewTopicModal","ChatLayout","narrow","title","setTitle","create"],"sources":["../src/chat/MessageView.tsx","../src/chat/useRecorder.ts","../src/chat/Chat.tsx","../src/chat/ConversationList.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Icon } from \"../components/Icon\";\nimport { Markdown } from \"../components/Markdown\";\nimport { UIKIT_NS } from \"../i18n\";\nimport type { ChatMessage, ChatStep } from \"./types\";\n\n/** The locale of Intl formatting for a language code of the products. */\nconst intlLocale = (lng: string) => (lng === \"zh\" ? \"zh-CN\" : lng);\n\n/** The time of a caption: hours and minutes in the language of the interface. */\nexport function markTime(iso: string, language: string): string {\n return new Intl.DateTimeFormat(intlLocale(language), { hour: \"2-digit\", minute: \"2-digit\" }).format(new Date(iso));\n}\n\n/** More than three steps are folded to the last two (design §6.4). */\nexport function visibleSteps(steps: ChatStep[], open: boolean): ChatStep[] {\n return open || steps.length <= 3 ? steps : steps.slice(-2);\n}\n\n/** The icon of a step and its colour class by the state of the step. */\nconst STEP = {\n done: { icon: \"check\", cls: \"ok\" },\n error: { icon: \"alert\", cls: \"er\" },\n running: { icon: \"clock\", cls: \"run\" },\n} as const;\n\n/** One message of the conversation (design §6.4). */\nexport function MessageView({ m, busy, onRetry }: { m: ChatMessage; busy: boolean; onRetry?: () => void }) {\n const { t, i18n } = useTranslation(UIKIT_NS);\n const [open, setOpen] = useState(false);\n if (m.role === \"note\") return <div className=\"sysnote\">{m.text}</div>;\n const mark = m.mark && <div className=\"chan\"><Icon name={m.mark.icon} size={12} /> {m.mark.text} · {markTime(m.createdAt, i18n.language)}</div>;\n const atts = m.attachments && m.attachments.length > 0 && (\n <div className=\"atts\">\n {m.attachments.map((a) => <a key={a.id} href={a.href}><Icon name=\"clip\" size={12} /> {a.fileName}</a>)}\n </div>\n );\n if (m.role === \"user\") {\n return (\n <div className=\"m u\">\n {mark}\n {m.content}\n {m.text && <div className=\"bub\">{m.text}</div>}\n {atts}\n {m.wait && (\n <div className={`turnstate${m.wait.state === \"queued\" ? \" q\" : \"\"}`} role=\"status\">\n {m.wait.state === \"queued\"\n ? <><Icon name=\"clock\" size={12} />{t(\"chat.wait.queued\", { count: m.wait.position })}</>\n : <><span className=\"spin\" aria-hidden=\"true\" />{t(\"chat.wait.starting\")}</>}\n </div>\n )}\n </div>\n );\n }\n const steps = m.steps ?? [];\n const waiting = (m.status === \"streaming\" || m.status === \"pending\") && !m.text && !m.content;\n return (\n <div className=\"m a\">\n {mark}\n {steps.length > 3 && (\n <button className=\"tools-fold\" onClick={() => setOpen((v) => !v)} aria-expanded={open}>\n {open ? t(\"chat.hideSteps\") : t(\"chat.moreSteps\", { count: steps.length - 2 })}\n </button>\n )}\n {visibleSteps(steps, open).map((s) => (\n <div key={s.id} className=\"tool\" title={s.title}>\n <Icon name={STEP[s.status].icon} size={14} className={STEP[s.status].cls} />\n <span className=\"tx\">{s.text}</span>\n </div>\n ))}\n {waiting && <div className=\"bub\"><span className=\"typing\" aria-label={t(\"chat.typing\")}><i /><i /><i /></span></div>}\n {m.content ?? (m.text && <div className=\"bub\"><Markdown text={m.text} /></div>)}\n {atts}\n {m.error && (\n <div className={`llmerr${m.error.transient ? \" amber\" : \"\"}`} role=\"alert\" style={{ marginTop: 6 }}>\n <span>{m.error.text}</span>\n {onRetry && (\n <div className=\"row\">\n <button className=\"btn sm\" disabled={busy} onClick={onRetry}><Icon name=\"refresh\" size={14} />{t(\"chat.retry\")}</button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\n/** Push-to-talk recording with MediaRecorder. */\nexport function useRecorder() {\n const supported = typeof window !== \"undefined\" && \"MediaRecorder\" in window && !!navigator.mediaDevices?.getUserMedia;\n const [recording, setRecording] = useState(false);\n const [seconds, setSeconds] = useState(0);\n const [error, setError] = useState(false);\n const rec = useRef<MediaRecorder | null>(null);\n const chunks = useRef<Blob[]>([]);\n const timer = useRef<number | undefined>(undefined);\n const stopped = useRef<((b: Blob | null) => void) | null>(null);\n\n const cleanup = useCallback(() => {\n window.clearInterval(timer.current);\n rec.current?.stream.getTracks().forEach((tr) => tr.stop());\n rec.current = null;\n setRecording(false);\n }, []);\n\n const start = useCallback(async () => {\n if (!supported || rec.current) return;\n setError(false);\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const r = new MediaRecorder(stream);\n chunks.current = [];\n r.ondataavailable = (e) => e.data.size > 0 && chunks.current.push(e.data);\n r.onstop = () => {\n const blob = chunks.current.length ? new Blob(chunks.current, { type: r.mimeType || \"audio/webm\" }) : null;\n cleanup();\n stopped.current?.(blob);\n stopped.current = null;\n };\n rec.current = r;\n r.start();\n setSeconds(0);\n setRecording(true);\n const began = Date.now();\n timer.current = window.setInterval(() => setSeconds(Math.floor((Date.now() - began) / 1000)), 250);\n } catch {\n setError(true);\n cleanup();\n }\n }, [supported, cleanup]);\n\n const stop = useCallback(() => new Promise<Blob | null>((resolve) => {\n const r = rec.current;\n if (!r || r.state === \"inactive\") return resolve(null);\n stopped.current = resolve;\n r.stop();\n }), []);\n\n useEffect(() => () => cleanup(), [cleanup]);\n\n const mm = Math.floor(seconds / 60);\n const ss = String(seconds % 60).padStart(2, \"0\");\n return { supported, recording, seconds: `${mm}:${ss}`, error, start, stop };\n}\n","import { useLayoutEffect, useRef, useState, type KeyboardEvent } from \"react\";\nimport { Icon } from \"../components/Icon\";\nimport { useToast } from \"../components/ui\";\nimport { useKitT } from \"../i18n\";\nimport { MessageView } from \"./MessageView\";\nimport type { ChatProps, ChatUpload } from \"./types\";\nimport { useRecorder } from \"./useRecorder\";\n\n/** The conversation: the messages and the composer (FTR.NAB.CMN-0005 R11–R13,\n * design §6). The behaviour is the one of the chat of Nabu. A control exists\n * only when the product gave its handler: no `onCancel` — no cancel button. */\nexport function Chat(props: ChatProps) {\n const t = useKitT();\n const toast = useToast();\n const [text, setText] = useState(\"\");\n const [pending, setPending] = useState<ChatUpload[]>([]);\n const [uploading, setUploading] = useState(false);\n const [transcript, setTranscript] = useState<string | null>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const fileRef = useRef<HTMLInputElement>(null);\n const recorder = useRecorder();\n const { messages, busy = false } = props;\n\n const first = messages[0]?.id;\n const tail = messages[messages.length - 1];\n const tailText = tail?.text.length;\n const tailSteps = tail?.steps?.length;\n // The first message and the height of the list at the last layout: earlier\n // messages loaded above it keep the place of the list.\n const lastLayout = useRef<{ first?: string; height: number }>({ height: 0 });\n // The list follows new messages and the growth of the last one: the dependencies are the trigger.\n useLayoutEffect(() => {\n const el = listRef.current;\n if (!el) return;\n const was = lastLayout.current;\n // history is prepended when the message that was first is still there, lower\n const prepended = was.first !== undefined && first !== was.first && messages.some((m) => m.id === was.first);\n el.scrollTop = prepended ? el.scrollTop + (el.scrollHeight - was.height) : el.scrollHeight;\n lastLayout.current = { first, height: el.scrollHeight };\n }, [messages.length, first, tailText, tailSteps]); // oxlint-disable-line react/exhaustive-effect-dependencies\n\n const send = async (body: string, voice: boolean) => {\n const msg = body.trim();\n if ((!msg && pending.length === 0) || busy || props.readOnly) return;\n try {\n await props.onSend(msg, pending.map((f) => f.id), voice);\n setText(\"\");\n setPending([]);\n setTranscript(null);\n } catch {\n // the product has told the user; the text stays in the composer\n }\n };\n\n const attach = async (files: FileList | null) => {\n if (!files?.length || !props.onUpload) return;\n setUploading(true);\n for (const f of Array.from(files)) {\n if (props.upload && f.size > props.upload.maxBytes) {\n toast({ kind: \"error\", title: t(\"chat.tooLarge\"), text: f.name });\n continue;\n }\n const a = await props.onUpload(f).catch(() => null);\n if (a) setPending((x) => [...x, a]);\n }\n setUploading(false);\n if (fileRef.current) fileRef.current.value = \"\";\n };\n\n const stopAndTranscribe = async () => {\n const blob = await recorder.stop();\n if (!blob || !props.onTranscribe) return;\n const r = await props.onTranscribe(blob).catch(() => null);\n if (r === null) return;\n if (!r) toast({ kind: \"error\", title: t(\"chat.voice.notRecognized\") });\n else setTranscript(r);\n };\n // Without a pointer the same button starts and stops the recording (R18).\n const voiceKey = (e: KeyboardEvent) => {\n if (e.key !== \" \" && e.key !== \"Enter\") return;\n e.preventDefault();\n if (e.repeat) return;\n if (recorder.recording) void stopAndTranscribe();\n else void recorder.start();\n };\n\n const placeholder = t(\"chat.placeholder\", { name: props.agentName });\n return (\n <section className=\"nb-chat\" aria-label={t(\"chat.title\")}>\n {props.header}\n <div className=\"msgs\" ref={listRef} aria-live=\"polite\">\n {props.hasOlder && props.onLoadOlder && (\n <button className=\"btn ghost sm\" style={{ alignSelf: \"center\" }} disabled={props.loadingOlder} onClick={props.onLoadOlder}>\n {t(\"chat.loadOlder\")}\n </button>\n )}\n {props.historyError && <div className=\"err-text\">{props.historyError}</div>}\n {messages.length === 0 && !props.loading && !props.historyError && (\n <div className=\"empty\"><div className=\"ic\"><Icon name=\"spark\" /></div><b>{t(\"chat.emptyTitle\", { name: props.agentName })}</b>{props.emptyHint}</div>\n )}\n {messages.map((m) => <MessageView key={m.id} m={m} busy={busy} onRetry={props.onRetry && (() => props.onRetry?.(m.id))} />)}\n {props.onConfirm && (props.confirmations ?? []).map((c) => (\n <div className=\"confirm\" key={c.id} role=\"group\" aria-label={t(\"chat.confirm.title\")}>\n <div className=\"h\"><Icon name=\"alert\" size={16} />{t(\"chat.confirm.title\")}</div>\n <div>{t(\"chat.confirm.text\", { action: c.summary })}</div>\n {c.preview !== undefined && <pre>{c.preview}</pre>}\n <div className=\"acts\">\n <button className=\"btn sm primary\" disabled={busy} onClick={() => props.onConfirm?.(c.id, true)}>{t(\"chat.confirm.approve\")}</button>\n <button className=\"btn sm\" disabled={busy} onClick={() => props.onConfirm?.(c.id, false)}>{t(\"chat.confirm.reject\")}</button>\n </div>\n </div>\n ))}\n </div>\n\n {props.readOnly ? (\n <div className=\"composer\" style={{ justifyContent: \"space-between\", alignItems: \"center\" }}>{props.readOnly}</div>\n ) : transcript !== null ? (\n <div className=\"transcript\" style={{ margin: 10 }}>\n <div className=\"small muted\" style={{ marginBottom: 4 }}>{t(\"chat.voice.review\")}</div>\n <textarea className=\"inp\" rows={3} value={transcript} onChange={(e) => setTranscript(e.target.value)} aria-label={t(\"chat.voice.review\")} />\n <div className=\"row2\" style={{ marginTop: 8 }}>\n <button className=\"btn ghost sm\" onClick={() => setTranscript(null)}>{t(\"delete\")}</button>\n <button className=\"btn primary sm\" disabled={!transcript.trim() || busy} onClick={() => send(transcript, true)}>{t(\"chat.send\")}</button>\n </div>\n </div>\n ) : (\n <>\n {props.aboveComposer}\n {pending.length > 0 && (\n <div className=\"pending-files\">\n {pending.map((f) => (\n <span key={f.id} className=\"chip\">\n <Icon name=\"clip\" size={12} />{f.fileName}\n <button className=\"iconbtn\" style={{ width: 18, height: 18 }} aria-label={t(\"remove\")}\n onClick={() => setPending((x) => x.filter((y) => y.id !== f.id))}><Icon name=\"x\" size={12} /></button>\n </span>\n ))}\n </div>\n )}\n <div className=\"composer\">\n {props.onUpload && (\n <>\n <button className=\"iconbtn\" aria-label={t(\"chat.attach\")} disabled={uploading} onClick={() => fileRef.current?.click()}><Icon name=\"clip\" /></button>\n <input ref={fileRef} type=\"file\" multiple hidden accept={props.upload?.accept} onChange={(e) => attach(e.target.files)} />\n </>\n )}\n {recorder.recording ? (\n <div className=\"rec-box\">{t(\"chat.voice.recording\", { seconds: recorder.seconds })}</div>\n ) : (\n <textarea rows={1} value={text} placeholder={placeholder} aria-label={placeholder} onChange={(e) => setText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey && !e.nativeEvent.isComposing) {\n e.preventDefault();\n void send(text, false);\n }\n }} />\n )}\n {props.onTranscribe && (\n <button className={`iconbtn${recorder.recording ? \" rec\" : \"\"}`} disabled={!recorder.supported}\n aria-label={recorder.recording ? t(\"chat.voice.stop\") : t(\"chat.voice.hold\")}\n title={recorder.supported ? t(\"chat.voice.hold\") : t(\"chat.voice.unsupported\")}\n onPointerDown={(e) => { e.preventDefault(); if (!recorder.recording) void recorder.start(); }}\n onPointerUp={() => recorder.recording && stopAndTranscribe()}\n onPointerLeave={() => recorder.recording && stopAndTranscribe()}\n onKeyDown={voiceKey}>\n <Icon name=\"mic\" />\n </button>\n )}\n {props.onCancel && busy ? (\n <button className=\"iconbtn fill\" aria-label={t(\"chat.stop\")} onClick={props.onCancel}><Icon name=\"stop\" /></button>\n ) : (\n <button className=\"iconbtn fill\" aria-label={t(\"chat.send\")} disabled={(!text.trim() && pending.length === 0) || busy} onClick={() => send(text, false)}>\n <Icon name=\"send\" />\n </button>\n )}\n </div>\n {recorder.error && <div className=\"err-text\" style={{ padding: \"0 12px 8px\" }}>{t(\"chat.voice.micDenied\")}</div>}\n </>\n )}\n </section>\n );\n}\n","import { useEffect, useState, type ReactNode } from \"react\";\nimport { Icon } from \"../components/Icon\";\nimport { Modal } from \"../components/ui\";\nimport { useKitT } from \"../i18n\";\nimport type { ChatConversation, ConversationListProps } from \"./types\";\n\n/** The conversations of the user: the main one, the topics, the archive (design\n * §6.2). In a wide place the list stands left of the messages; on a phone and in\n * a narrow place it lies over them while open and closes by a pick, a tap\n * beside it or Escape. */\nexport function ConversationList({ conversations, currentId, open, onPick, onClose, onCreate }: ConversationListProps) {\n const t = useKitT();\n const [creating, setCreating] = useState(false);\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && onClose();\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [open, onClose]);\n\n const item = (c: ChatConversation) => (\n <button key={c.id} className={`it${c.id === currentId ? \" on\" : \"\"}${c.group === \"archive\" ? \" arch\" : \"\"}`}\n onClick={() => onPick(c.id)} aria-current={c.id === currentId}>\n <Icon name={c.icon ?? \"msg2\"} size={16} />\n <span className=\"nm\">{c.title}</span>\n {!!c.unread && <span className=\"unread\" aria-label={t(\"chat.unread\", { count: c.unread })}>{c.unread}</span>}\n <span className=\"sub\">{c.when}</span>\n </button>\n );\n const inGroup = (g: ChatConversation[\"group\"]) => conversations.filter((c) => c.group === g);\n const topics = inGroup(\"topic\");\n const archive = inGroup(\"archive\");\n return (\n <>\n <nav className={`nb-side${open ? \" open\" : \"\"}`} aria-label={t(\"chat.conversations\")}>\n {onCreate && (\n <button className=\"btn sm\" style={{ width: \"100%\", justifyContent: \"center\" }} onClick={() => setCreating(true)}>\n <Icon name=\"plus\" size={15} />{t(\"chat.newTopic\")}\n </button>\n )}\n <div className=\"h\">{t(\"chat.conversation\")}</div>\n {inGroup(\"main\").map(item)}\n <div className=\"h\">{t(\"chat.topics\")}</div>\n {topics.length === 0 && <div className=\"small muted\" style={{ padding: \"0 10px\" }}>{t(\"chat.noTopics\")}</div>}\n {topics.map(item)}\n {archive.length > 0 && <div className=\"h\">{t(\"chat.archive\")}</div>}\n {archive.map(item)}\n </nav>\n {/* over the messages: a tap beside the list closes it */}\n {open && <button type=\"button\" className=\"nb-scrim\" aria-label={t(\"close\")} onClick={onClose} />}\n {creating && onCreate && <NewTopicModal onClose={() => setCreating(false)} onCreate={onCreate} />}\n </>\n );\n}\n\n/** The place of a chat: the list of conversations, the conversation and what the\n * product puts beside them. `narrow` is for a side panel: the list lies over the\n * messages as on a phone, whatever the width of the window. */\nexport function ChatLayout({ narrow, children }: { narrow?: boolean; children: ReactNode }) {\n return <div className={`nb-app${narrow ? \" narrow\" : \"\"}`}>{children}</div>;\n}\n\n/** The dialog of a new topic: one field, Enter creates. */\nexport function NewTopicModal({ onClose, onCreate }: { onClose: () => void; onCreate: (title: string) => Promise<void> }) {\n const t = useKitT();\n const [title, setTitle] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const create = async () => {\n if (!title.trim() || pending) return;\n setPending(true);\n setError(null);\n try {\n await onCreate(title.trim());\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n setPending(false);\n }\n };\n return (\n <Modal title={t(\"chat.newTopic\")} onClose={onClose} footer={<>\n <button className=\"btn ghost\" onClick={onClose}>{t(\"cancel\")}</button>\n <button className=\"btn primary\" disabled={!title.trim() || pending} onClick={create}>{t(\"create\")}</button>\n </>}>\n <div className=\"field\">\n <label htmlFor=\"topic-title\">{t(\"chat.topicTitle\")}</label>\n <input id=\"topic-title\" className=\"inp\" value={title} maxLength={120} onChange={(e) => setTitle(e.target.value)}\n onKeyDown={(e) => e.key === \"Enter\" && create()} />\n <div className=\"hint\">{t(\"chat.topicHint\")}</div>\n </div>\n {error && <div className=\"err-text\">{error}</div>}\n </Modal>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat.js","names":["_Fragment","_Fragment","_Fragment","useToast","Icon","Modal","Markdown","useKitT","UIKIT_NS","intlLocale","lng","markTime","language","iso","visibleSteps","open","steps","STEP","MessageView","useTranslation","setOpen","useState","m","mark","i18n","atts","a","Fragment","t","waiting","v","s","onRetry","busy","useRecorder","supported","recording","setRecording","seconds","setSeconds","error","setError","rec","useRef","chunks","timer","stopped","cleanup","useCallback","tr","start","stream","r","blob","began","stop","resolve","useEffect","Chat","toast","text","setText","pending","setPending","uploading","setUploading","transcript","setTranscript","listRef","fileRef","recorder","props","first","messages","tail","last","tailText","tailSteps","lastLayout","el","was","send","msg","body","f","voice","attach","files","x","stopAndTranscribe","voiceKey","placeholder","c","y","ConversationList","creating","setCreating","onKey","onClose","item","currentId","onPick","inGroup","conversations","g","topics","archive","onCreate","NewTopicModal","ChatLayout","narrow","title","setTitle","create"],"sources":["../src/chat/MessageView.tsx","../src/chat/useRecorder.ts","../src/chat/Chat.tsx","../src/chat/ConversationList.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { Icon } from \"../components/Icon\";\nimport { Markdown } from \"../components/Markdown\";\nimport { UIKIT_NS } from \"../i18n\";\nimport type { ChatMessage, ChatStep } from \"./types\";\n\n/** The locale of Intl formatting for a language code of the products. */\nconst intlLocale = (lng: string) => (lng === \"zh\" ? \"zh-CN\" : lng);\n\n/** The time of a caption: hours and minutes in the language of the interface. */\nexport function markTime(iso: string, language: string): string {\n return new Intl.DateTimeFormat(intlLocale(language), { hour: \"2-digit\", minute: \"2-digit\" }).format(new Date(iso));\n}\n\n/** More than three steps are folded to the last two (design §6.4). */\nexport function visibleSteps(steps: ChatStep[], open: boolean): ChatStep[] {\n return open || steps.length <= 3 ? steps : steps.slice(-2);\n}\n\n/** The icon of a step and its colour class by the state of the step. */\nconst STEP = {\n done: { icon: \"check\", cls: \"ok\" },\n error: { icon: \"alert\", cls: \"er\" },\n running: { icon: \"clock\", cls: \"run\" },\n} as const;\n\n/** One message of the conversation (design §6.4). */\nexport function MessageView({ m, busy, onRetry }: { m: ChatMessage; busy: boolean; onRetry?: () => void }) {\n const { t, i18n } = useTranslation(UIKIT_NS);\n const [open, setOpen] = useState(false);\n if (m.role === \"note\") return <div className=\"sysnote\">{m.text}</div>;\n const mark = m.mark && <div className=\"chan\"><Icon name={m.mark.icon} size={12} /> {m.mark.text} · {markTime(m.createdAt, i18n.language)}</div>;\n const atts = m.attachments && m.attachments.length > 0 && (\n <div className=\"atts\">\n {m.attachments.map((a) => <a key={a.id} href={a.href}><Icon name=\"clip\" size={12} /> {a.fileName}</a>)}\n </div>\n );\n if (m.role === \"user\") {\n return (\n <div className=\"m u\">\n {mark}\n {m.content}\n {m.text && <div className=\"bub\">{m.text}</div>}\n {atts}\n {m.wait && (\n <div className={`turnstate${m.wait.state === \"queued\" ? \" q\" : \"\"}`} role=\"status\">\n {m.wait.state === \"queued\"\n ? <><Icon name=\"clock\" size={12} />{t(\"chat.wait.queued\", { count: m.wait.position })}</>\n : <><span className=\"spin\" aria-hidden=\"true\" />{t(\"chat.wait.starting\")}</>}\n </div>\n )}\n </div>\n );\n }\n const steps = m.steps ?? [];\n const waiting = (m.status === \"streaming\" || m.status === \"pending\") && !m.text && !m.content;\n return (\n <div className=\"m a\">\n {mark}\n {steps.length > 3 && (\n <button className=\"tools-fold\" onClick={() => setOpen((v) => !v)} aria-expanded={open}>\n {open ? t(\"chat.hideSteps\") : t(\"chat.moreSteps\", { count: steps.length - 2 })}\n </button>\n )}\n {visibleSteps(steps, open).map((s) => (\n <div key={s.id} className=\"tool\" title={s.title}>\n <Icon name={STEP[s.status].icon} size={14} className={STEP[s.status].cls} />\n <span className=\"tx\">{s.text}</span>\n </div>\n ))}\n {waiting && <div className=\"bub\"><span className=\"typing\" aria-label={t(\"chat.typing\")}><i /><i /><i /></span></div>}\n {m.content ?? (m.text && <div className=\"bub\"><Markdown text={m.text} /></div>)}\n {atts}\n {m.error && (\n <div className={`llmerr${m.error.transient ? \" amber\" : \"\"}`} role=\"alert\" style={{ marginTop: 6 }}>\n <span>{m.error.text}</span>\n {onRetry && (\n <div className=\"row\">\n <button className=\"btn sm\" disabled={busy} onClick={onRetry}><Icon name=\"refresh\" size={14} />{t(\"chat.retry\")}</button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\n/** Push-to-talk recording with MediaRecorder. */\nexport function useRecorder() {\n const supported = typeof window !== \"undefined\" && \"MediaRecorder\" in window && !!navigator.mediaDevices?.getUserMedia;\n const [recording, setRecording] = useState(false);\n const [seconds, setSeconds] = useState(0);\n const [error, setError] = useState(false);\n const rec = useRef<MediaRecorder | null>(null);\n const chunks = useRef<Blob[]>([]);\n const timer = useRef<number | undefined>(undefined);\n const stopped = useRef<((b: Blob | null) => void) | null>(null);\n\n const cleanup = useCallback(() => {\n window.clearInterval(timer.current);\n rec.current?.stream.getTracks().forEach((tr) => tr.stop());\n rec.current = null;\n setRecording(false);\n }, []);\n\n const start = useCallback(async () => {\n if (!supported || rec.current) return;\n setError(false);\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const r = new MediaRecorder(stream);\n chunks.current = [];\n r.ondataavailable = (e) => e.data.size > 0 && chunks.current.push(e.data);\n r.onstop = () => {\n const blob = chunks.current.length ? new Blob(chunks.current, { type: r.mimeType || \"audio/webm\" }) : null;\n cleanup();\n stopped.current?.(blob);\n stopped.current = null;\n };\n rec.current = r;\n r.start();\n setSeconds(0);\n setRecording(true);\n const began = Date.now();\n timer.current = window.setInterval(() => setSeconds(Math.floor((Date.now() - began) / 1000)), 250);\n } catch {\n setError(true);\n cleanup();\n }\n }, [supported, cleanup]);\n\n const stop = useCallback(() => new Promise<Blob | null>((resolve) => {\n const r = rec.current;\n if (!r || r.state === \"inactive\") return resolve(null);\n stopped.current = resolve;\n r.stop();\n }), []);\n\n useEffect(() => () => cleanup(), [cleanup]);\n\n const mm = Math.floor(seconds / 60);\n const ss = String(seconds % 60).padStart(2, \"0\");\n return { supported, recording, seconds: `${mm}:${ss}`, error, start, stop };\n}\n","import { useLayoutEffect, useRef, useState, type KeyboardEvent } from \"react\";\nimport { Icon } from \"../components/Icon\";\nimport { useToast } from \"../components/ui\";\nimport { useKitT } from \"../i18n\";\nimport { MessageView } from \"./MessageView\";\nimport type { ChatProps, ChatUpload } from \"./types\";\nimport { useRecorder } from \"./useRecorder\";\n\n/** The conversation: the messages and the composer (FTR.NAB.CMN-0005 R11–R13,\n * design §6). The behaviour is the one of the chat of Nabu. A control exists\n * only when the product gave its handler: no `onCancel` — no cancel button. */\nexport function Chat(props: ChatProps) {\n const t = useKitT();\n const toast = useToast();\n const [text, setText] = useState(\"\");\n const [pending, setPending] = useState<ChatUpload[]>([]);\n const [uploading, setUploading] = useState(false);\n const [transcript, setTranscript] = useState<string | null>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const fileRef = useRef<HTMLInputElement>(null);\n const recorder = useRecorder();\n const { messages, busy = false } = props;\n\n const first = messages[0]?.id;\n const tail = messages[messages.length - 1];\n const last = tail?.id;\n const tailText = tail?.text.length;\n const tailSteps = tail?.steps?.length;\n // The ends and the height of the list at the last layout: earlier messages\n // loaded above it keep the place of the list.\n const lastLayout = useRef<{ first?: string; last?: string; height: number }>({ height: 0 });\n // The list follows new messages and the growth of the last one: the dependencies are the trigger.\n useLayoutEffect(() => {\n const el = listRef.current;\n if (!el) return;\n const was = lastLayout.current;\n // history is prepended when the start of the list changed and its end did not;\n // another conversation changes both\n const prepended = was.first !== undefined && first !== was.first && last === was.last;\n el.scrollTop = prepended ? el.scrollTop + (el.scrollHeight - was.height) : el.scrollHeight;\n lastLayout.current = { first, last, height: el.scrollHeight };\n // oxlint-disable-next-line react/exhaustive-effect-dependencies -- the length and the growth of the tail only trigger\n }, [messages.length, first, last, tailText, tailSteps]);\n\n const send = async (body: string, voice: boolean) => {\n const msg = body.trim();\n if ((!msg && pending.length === 0) || busy || props.readOnly) return;\n try {\n await props.onSend(msg, pending.map((f) => f.id), voice);\n setText(\"\");\n setPending([]);\n setTranscript(null);\n } catch {\n // the product has told the user; the text stays in the composer\n }\n };\n\n const attach = async (files: FileList | null) => {\n if (!files?.length || !props.onUpload) return;\n setUploading(true);\n for (const f of Array.from(files)) {\n if (props.upload && f.size > props.upload.maxBytes) {\n toast({ kind: \"error\", title: t(\"chat.tooLarge\"), text: f.name });\n continue;\n }\n const a = await props.onUpload(f).catch(() => null);\n if (a) setPending((x) => [...x, a]);\n }\n setUploading(false);\n if (fileRef.current) fileRef.current.value = \"\";\n };\n\n const stopAndTranscribe = async () => {\n const blob = await recorder.stop();\n if (!blob || !props.onTranscribe) return;\n const r = await props.onTranscribe(blob).catch(() => null);\n if (r === null) return;\n if (!r) toast({ kind: \"error\", title: t(\"chat.voice.notRecognized\") });\n else setTranscript(r);\n };\n // Without a pointer the same button starts and stops the recording (R18).\n const voiceKey = (e: KeyboardEvent) => {\n if (e.key !== \" \" && e.key !== \"Enter\") return;\n e.preventDefault();\n if (e.repeat) return;\n if (recorder.recording) void stopAndTranscribe();\n else void recorder.start();\n };\n\n const placeholder = t(\"chat.placeholder\", { name: props.agentName });\n return (\n <section className=\"nb-chat\" aria-label={t(\"chat.title\")}>\n {props.header}\n <div className=\"msgs\" ref={listRef} aria-live=\"polite\">\n {props.hasOlder && props.onLoadOlder && (\n <button className=\"btn ghost sm\" style={{ alignSelf: \"center\" }} disabled={props.loadingOlder} onClick={props.onLoadOlder}>\n {t(\"chat.loadOlder\")}\n </button>\n )}\n {props.historyError && <div className=\"err-text\">{props.historyError}</div>}\n {messages.length === 0 && !props.loading && !props.historyError && (\n <div className=\"empty\"><div className=\"ic\"><Icon name=\"spark\" /></div><b>{t(\"chat.emptyTitle\", { name: props.agentName })}</b>{props.emptyHint}</div>\n )}\n {messages.map((m) => <MessageView key={m.id} m={m} busy={busy} onRetry={props.onRetry && (() => props.onRetry?.(m.id))} />)}\n {props.onConfirm && (props.confirmations ?? []).map((c) => (\n <div className=\"confirm\" key={c.id} role=\"group\" aria-label={t(\"chat.confirm.title\")}>\n <div className=\"h\"><Icon name=\"alert\" size={16} />{t(\"chat.confirm.title\")}</div>\n <div>{t(\"chat.confirm.text\", { action: c.summary })}</div>\n {c.preview !== undefined && <pre>{c.preview}</pre>}\n <div className=\"acts\">\n {/* never tied to `busy`: the agent asks in the middle of its turn */}\n <button className=\"btn sm primary\" onClick={() => props.onConfirm?.(c.id, true)}>{t(\"chat.confirm.approve\")}</button>\n <button className=\"btn sm\" onClick={() => props.onConfirm?.(c.id, false)}>{t(\"chat.confirm.reject\")}</button>\n </div>\n </div>\n ))}\n </div>\n\n {props.readOnly ? (\n <div className=\"composer\" style={{ justifyContent: \"space-between\", alignItems: \"center\" }}>{props.readOnly}</div>\n ) : transcript !== null ? (\n <div className=\"transcript\" style={{ margin: 10 }}>\n <div className=\"small muted\" style={{ marginBottom: 4 }}>{t(\"chat.voice.review\")}</div>\n <textarea className=\"inp\" rows={3} value={transcript} onChange={(e) => setTranscript(e.target.value)} aria-label={t(\"chat.voice.review\")} />\n <div className=\"row2\" style={{ marginTop: 8 }}>\n <button className=\"btn ghost sm\" onClick={() => setTranscript(null)}>{t(\"delete\")}</button>\n <button className=\"btn primary sm\" disabled={!transcript.trim() || busy} onClick={() => send(transcript, true)}>{t(\"chat.send\")}</button>\n </div>\n </div>\n ) : (\n <>\n {props.aboveComposer}\n {pending.length > 0 && (\n <div className=\"pending-files\">\n {pending.map((f) => (\n <span key={f.id} className=\"chip\">\n <Icon name=\"clip\" size={12} />{f.fileName}\n <button className=\"iconbtn\" style={{ width: 18, height: 18 }} aria-label={t(\"remove\")}\n onClick={() => setPending((x) => x.filter((y) => y.id !== f.id))}><Icon name=\"x\" size={12} /></button>\n </span>\n ))}\n </div>\n )}\n <div className=\"composer\">\n {props.onUpload && (\n <>\n <button className=\"iconbtn\" aria-label={t(\"chat.attach\")} disabled={uploading} onClick={() => fileRef.current?.click()}><Icon name=\"clip\" /></button>\n <input ref={fileRef} type=\"file\" multiple hidden accept={props.upload?.accept} onChange={(e) => attach(e.target.files)} />\n </>\n )}\n {recorder.recording ? (\n <div className=\"rec-box\">{t(\"chat.voice.recording\", { seconds: recorder.seconds })}</div>\n ) : (\n <textarea rows={1} value={text} placeholder={placeholder} aria-label={placeholder} onChange={(e) => setText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey && !e.nativeEvent.isComposing) {\n e.preventDefault();\n void send(text, false);\n }\n }} />\n )}\n {props.onTranscribe && (\n <button className={`iconbtn${recorder.recording ? \" rec\" : \"\"}`} disabled={!recorder.supported}\n aria-label={recorder.recording ? t(\"chat.voice.stop\") : t(\"chat.voice.hold\")}\n title={recorder.supported ? t(\"chat.voice.hold\") : t(\"chat.voice.unsupported\")}\n onPointerDown={(e) => { e.preventDefault(); if (!recorder.recording) void recorder.start(); }}\n onPointerUp={() => recorder.recording && stopAndTranscribe()}\n onPointerLeave={() => recorder.recording && stopAndTranscribe()}\n onKeyDown={voiceKey}>\n <Icon name=\"mic\" />\n </button>\n )}\n {props.onCancel && busy ? (\n <button className=\"iconbtn fill\" aria-label={t(\"chat.stop\")} onClick={props.onCancel}><Icon name=\"stop\" /></button>\n ) : (\n <button className=\"iconbtn fill\" aria-label={t(\"chat.send\")} disabled={(!text.trim() && pending.length === 0) || busy} onClick={() => send(text, false)}>\n <Icon name=\"send\" />\n </button>\n )}\n </div>\n {recorder.error && <div className=\"err-text\" style={{ padding: \"0 12px 8px\" }}>{t(\"chat.voice.micDenied\")}</div>}\n </>\n )}\n </section>\n );\n}\n","import { useEffect, useState, type ReactNode } from \"react\";\nimport { Icon } from \"../components/Icon\";\nimport { Modal } from \"../components/ui\";\nimport { useKitT } from \"../i18n\";\nimport type { ChatConversation, ConversationListProps } from \"./types\";\n\n/** The conversations of the user: the main one, the topics, the archive (design\n * §6.2). In a wide place the list stands left of the messages; on a phone and in\n * a narrow place it lies over them while open and closes by a pick, a tap\n * beside it or Escape. */\nexport function ConversationList({ conversations, currentId, open, onPick, onClose, onCreate }: ConversationListProps) {\n const t = useKitT();\n const [creating, setCreating] = useState(false);\n useEffect(() => {\n if (!open) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && onClose();\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [open, onClose]);\n\n const item = (c: ChatConversation) => (\n <button key={c.id} className={`it${c.id === currentId ? \" on\" : \"\"}${c.group === \"archive\" ? \" arch\" : \"\"}`}\n onClick={() => onPick(c.id)} aria-current={c.id === currentId}>\n <Icon name={c.icon ?? \"msg2\"} size={16} />\n <span className=\"nm\">{c.title}</span>\n {!!c.unread && <span className=\"unread\" aria-label={t(\"chat.unread\", { count: c.unread })}>{c.unread}</span>}\n <span className=\"sub\">{c.when}</span>\n </button>\n );\n const inGroup = (g: ChatConversation[\"group\"]) => conversations.filter((c) => c.group === g);\n const topics = inGroup(\"topic\");\n const archive = inGroup(\"archive\");\n return (\n <>\n <nav className={`nb-side${open ? \" open\" : \"\"}`} aria-label={t(\"chat.conversations\")}>\n {onCreate && (\n <button className=\"btn sm\" style={{ width: \"100%\", justifyContent: \"center\" }} onClick={() => setCreating(true)}>\n <Icon name=\"plus\" size={15} />{t(\"chat.newTopic\")}\n </button>\n )}\n <div className=\"h\">{t(\"chat.conversation\")}</div>\n {inGroup(\"main\").map(item)}\n <div className=\"h\">{t(\"chat.topics\")}</div>\n {topics.length === 0 && <div className=\"small muted\" style={{ padding: \"0 10px\" }}>{t(\"chat.noTopics\")}</div>}\n {topics.map(item)}\n {archive.length > 0 && <div className=\"h\">{t(\"chat.archive\")}</div>}\n {archive.map(item)}\n </nav>\n {/* over the messages: a tap beside the list closes it */}\n {open && <button type=\"button\" className=\"nb-scrim\" aria-label={t(\"close\")} onClick={onClose} />}\n {creating && onCreate && <NewTopicModal onClose={() => setCreating(false)} onCreate={onCreate} />}\n </>\n );\n}\n\n/** The place of a chat: the list of conversations, the conversation and what the\n * product puts beside them. `narrow` is for a side panel: the list lies over the\n * messages as on a phone, whatever the width of the window. */\nexport function ChatLayout({ narrow, children }: { narrow?: boolean; children: ReactNode }) {\n return <div className={`nb-app${narrow ? \" narrow\" : \"\"}`}>{children}</div>;\n}\n\n/** The dialog of a new topic: one field, Enter creates. */\nexport function NewTopicModal({ onClose, onCreate }: { onClose: () => void; onCreate: (title: string) => Promise<void> }) {\n const t = useKitT();\n const [title, setTitle] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const create = async () => {\n if (!title.trim() || pending) return;\n setPending(true);\n setError(null);\n try {\n await onCreate(title.trim());\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n setPending(false);\n }\n };\n return (\n <Modal title={t(\"chat.newTopic\")} onClose={onClose} footer={<>\n <button className=\"btn ghost\" onClick={onClose}>{t(\"cancel\")}</button>\n <button className=\"btn primary\" disabled={!title.trim() || pending} onClick={create}>{t(\"create\")}</button>\n </>}>\n <div className=\"field\">\n <label htmlFor=\"topic-title\">{t(\"chat.topicTitle\")}</label>\n <input id=\"topic-title\" className=\"inp\" value={title} maxLength={120} onChange={(e) => setTitle(e.target.value)}\n onKeyDown={(e) => e.key === \"Enter\" && create()} />\n <div className=\"hint\">{t(\"chat.topicHint\")}</div>\n </div>\n {error && <div className=\"err-text\">{error}</div>}\n </Modal>\n );\n}\n"],"mappings":"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"}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED