laif-ds
Version:
Design System di Laif con componenti React basati su principi di Atomic Design
342 lines (341 loc) • 11.3 kB
JavaScript
"use client";
import { jsxs as o, jsx as e } from "react/jsx-runtime";
import { AnimatePresence as v, motion as w } from "framer-motion";
import { useState as y, useEffect as S, useRef as Q } from "react";
import { omit as T } from "../../node_modules/remeda/dist/omit-HZOiLuMO.js";
import { AudioVisualizer as X } from "./audio-visualizer.js";
import { Button as g } from "./button.js";
import { FilePreview as Y } from "./file-preview.js";
import { InterruptPrompt as Z } from "./interrupt-prompt.js";
import { useAudioRecording as _ } from "../../hooks/use-audio-recording.js";
import { useAutosizeTextArea as $ } from "../../hooks/use-autosize-textarea.js";
import { cn as C } from "../../lib/utils.js";
import P from "../../node_modules/lucide-react/dist/esm/icons/paperclip.js";
import G from "../../node_modules/lucide-react/dist/esm/icons/mic.js";
import ee from "../../node_modules/lucide-react/dist/esm/icons/square.js";
import te from "../../node_modules/lucide-react/dist/esm/icons/arrow-up.js";
import re from "../../node_modules/lucide-react/dist/esm/icons/info.js";
import ie from "../../node_modules/lucide-react/dist/esm/icons/loader-circle.js";
function ae({
placeholder: i = "Ask AI...",
className: n,
onKeyDown: d,
submitOnEnter: l = !0,
stop: s,
isGenerating: u,
enableInterrupt: A = !0,
transcribeAudio: I,
"data-testid": R,
...r
}) {
const [F, x] = y(!1), [N, f] = y(!1), {
isListening: L,
isSpeechSupported: M,
isRecording: D,
isTranscribing: O,
audioStream: j,
toggleListening: q,
stopRecording: k
} = _({
transcribeAudio: I,
onTranscriptionComplete: (t) => {
r.onChange?.({ target: { value: t } });
}
});
S(() => {
u || f(!1);
}, [u]);
const h = (t) => {
r.allowAttachments && r.setFiles((a) => a === null ? t : t === null ? a : [...a, ...t]);
}, E = (t) => {
r.allowAttachments === !0 && (t.preventDefault(), x(!0));
}, H = (t) => {
r.allowAttachments === !0 && (t.preventDefault(), x(!1));
}, K = (t) => {
if (x(!1), r.allowAttachments !== !0) return;
t.preventDefault();
const a = t.dataTransfer;
a.files.length && h(Array.from(a.files));
}, U = (t) => {
const a = t.clipboardData?.items;
if (!a) return;
const c = t.clipboardData.getData("text");
if (c && c.length > 500 && r.allowAttachments) {
t.preventDefault();
const m = new Blob([c], { type: "text/plain" }), J = new File([m], "Pasted text", {
type: "text/plain",
lastModified: Date.now()
});
h([J]);
return;
}
const b = Array.from(a).map((m) => m.getAsFile()).filter((m) => m !== null);
r.allowAttachments && b.length > 0 && h(b);
}, V = (t) => {
if (l && t.key === "Enter" && !t.shiftKey) {
if (t.preventDefault(), u && s && A) {
if (N)
s(), f(!1), t.currentTarget.form?.requestSubmit();
else if (r.value || r.allowAttachments && r.files?.length) {
f(!0);
return;
}
}
t.currentTarget.form?.requestSubmit();
}
d?.(t);
}, p = Q(
null
), [B, W] = y(0);
S(() => {
p.current && W(p.current.offsetHeight);
}, [r.value]);
const z = r.allowAttachments && r.files && r.files.length > 0;
return $({
ref: p,
maxHeight: 240,
borderWidth: 1,
dependencies: [r.value, z]
}), /* @__PURE__ */ o(
"div",
{
className: "relative flex w-full",
onDragOver: E,
onDragLeave: H,
onDrop: K,
children: [
A && /* @__PURE__ */ e(
Z,
{
isOpen: N,
close: () => f(!1)
}
),
/* @__PURE__ */ e(
se,
{
isVisible: D,
onStopRecording: k
}
),
/* @__PURE__ */ e("div", { className: "relative flex w-full items-center space-x-2", children: /* @__PURE__ */ o("div", { className: "relative flex-1", children: [
/* @__PURE__ */ e(
"textarea",
{
"aria-label": "Write your prompt here",
placeholder: i,
ref: p,
onPaste: U,
onKeyDown: V,
"data-testid": R,
className: C(
"border-d-input bg-d-input ring-offset-d-background placeholder:text-d-muted-foreground focus-visible:border-d-primary z-10 w-full grow resize-none rounded-xl border p-3 pr-24 text-sm transition-[border] focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
z && "pb-16",
n
),
...r.allowAttachments ? T(r, ["allowAttachments", "files", "setFiles"]) : T(r, ["allowAttachments"])
}
),
r.allowAttachments && /* @__PURE__ */ e("div", { className: "absolute inset-x-3 bottom-2 z-20 overflow-x-scroll py-3 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ e("div", { className: "flex space-x-3", children: /* @__PURE__ */ e(v, { mode: "popLayout", children: r.files?.map((t) => /* @__PURE__ */ e(
Y,
{
file: t,
onRemove: () => {
r.setFiles((a) => {
if (!a) return null;
const c = Array.from(a).filter(
(b) => b !== t
);
return c.length === 0 ? null : c;
});
}
},
t.name + String(t.lastModified)
)) }) }) })
] }) }),
/* @__PURE__ */ o("div", { className: "absolute top-3 right-3 z-20 flex gap-2", children: [
r.allowAttachments && /* @__PURE__ */ e(
g,
{
type: "button",
size: "icon",
variant: "outline",
className: "h-8 w-8",
"aria-label": "Attach a file",
onClick: async () => {
const t = await le();
h(t);
},
children: /* @__PURE__ */ e(P, { className: "h-4 w-4" })
}
),
M && /* @__PURE__ */ e(
g,
{
type: "button",
variant: "outline",
className: C("h-8 w-8", L && "text-d-primary"),
"aria-label": "Voice input",
size: "icon",
onClick: q,
children: /* @__PURE__ */ e(G, { className: "h-4 w-4" })
}
),
u && s ? /* @__PURE__ */ e(
g,
{
type: "button",
size: "icon",
className: "h-8 w-8",
"aria-label": "Stop generating",
onClick: s,
children: /* @__PURE__ */ e(ee, { className: "h-3 w-3 animate-pulse", fill: "currentColor" })
}
) : /* @__PURE__ */ e(
g,
{
type: "submit",
size: "icon",
className: "h-8 w-8 transition-opacity",
"aria-label": "Send message",
disabled: r.value === "" || u,
children: /* @__PURE__ */ e(te, { className: "h-5 w-5" })
}
)
] }),
r.allowAttachments && /* @__PURE__ */ e(ne, { isDragging: F }),
/* @__PURE__ */ e(
ce,
{
isRecording: D,
isTranscribing: O,
audioStream: j,
textAreaHeight: B,
onStopRecording: k
}
)
]
}
);
}
ae.displayName = "MessageInput";
function ne({ isDragging: i }) {
return /* @__PURE__ */ e(v, { children: i && /* @__PURE__ */ o(
w.div,
{
className: "border-d-border bg-d-background text-d-secondary-foreground pointer-events-none absolute inset-0 z-20 flex items-center justify-center space-x-2 rounded-xl border border-dashed text-sm",
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
transition: { duration: 0.2 },
"aria-hidden": !0,
children: [
/* @__PURE__ */ e(P, { className: "h-4 w-4" }),
/* @__PURE__ */ e("span", { children: "Drop your files here to attach them." })
]
}
) });
}
function le() {
if (typeof document > "u")
return Promise.resolve(null);
const i = document.createElement("input");
return i.type = "file", i.multiple = !0, i.accept = "*/*", i.click(), new Promise((n) => {
i.onchange = (d) => {
const l = d.currentTarget.files;
if (l) {
n(Array.from(l));
return;
}
n(null);
};
});
}
function oe() {
return /* @__PURE__ */ o(
w.div,
{
className: "bg-d-background/80 flex h-full w-full flex-col items-center justify-center rounded-xl backdrop-blur-sm",
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 },
transition: { duration: 0.2 },
children: [
/* @__PURE__ */ o("div", { className: "relative", children: [
/* @__PURE__ */ e(ie, { className: "text-d-primary h-8 w-8 animate-spin" }),
/* @__PURE__ */ e(
w.div,
{
className: "bg-d-primary/20 absolute inset-0 h-8 w-8 animate-pulse rounded-full",
initial: { scale: 0.8, opacity: 0 },
animate: { scale: 1.2, opacity: 1 },
transition: {
duration: 1,
repeat: 1 / 0,
repeatType: "reverse",
ease: "easeInOut"
}
}
)
] }),
/* @__PURE__ */ e("p", { className: "text-d-secondary-foreground mt-4 text-sm font-medium", children: "Transcribing audio..." })
]
}
);
}
function se({ isVisible: i, onStopRecording: n }) {
return /* @__PURE__ */ e(v, { children: i && /* @__PURE__ */ e(
w.div,
{
initial: { top: 0, filter: "blur(5px)" },
animate: {
top: -40,
filter: "blur(0px)",
transition: {
type: "spring",
filter: { type: "tween" }
}
},
exit: { top: 0, filter: "blur(5px)" },
className: "bg-d-background border-d-border text-d-secondary-foreground absolute left-1/2 flex -translate-x-1/2 cursor-pointer overflow-hidden rounded-full border py-1 text-center text-sm whitespace-nowrap",
onClick: n,
children: /* @__PURE__ */ o("span", { className: "mx-2.5 flex items-center", children: [
/* @__PURE__ */ e(re, { className: "mr-2 h-3 w-3" }),
"Click to finish recording"
] })
}
) });
}
function ce({
isRecording: i,
isTranscribing: n,
audioStream: d,
textAreaHeight: l,
onStopRecording: s
}) {
return i ? /* @__PURE__ */ e(
"div",
{
className: "absolute inset-[1px] z-50 overflow-hidden rounded-xl",
style: { height: l - 2 },
children: /* @__PURE__ */ e(
X,
{
stream: d,
isRecording: i,
onClick: s
}
)
}
) : n ? /* @__PURE__ */ e(
"div",
{
className: "absolute inset-[1px] z-50 overflow-hidden rounded-xl",
style: { height: l - 2 },
children: /* @__PURE__ */ e(oe, {})
}
) : null;
}
export {
ae as MessageInput
};