@vaia-lab/sdk 0.2.0 → 0.4.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.
@@ -0,0 +1,1240 @@
1
+ // @vaia/sdk — VAIA Platform Integration SDK
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/react/index.ts
22
+ var react_exports = {};
23
+ __export(react_exports, {
24
+ HandeiaAgent: () => HandeiaAgent,
25
+ InputBar: () => InputBar,
26
+ MODELS: () => MODELS,
27
+ VoiceCanvas: () => VoiceCanvas
28
+ });
29
+ module.exports = __toCommonJS(react_exports);
30
+
31
+ // src/react/agent.tsx
32
+ var import_react3 = require("react");
33
+ var import_react4 = require("motion/react");
34
+ var import_lucide_react2 = require("lucide-react");
35
+
36
+ // src/react/input-bar.tsx
37
+ var import_react = require("react");
38
+ var import_react2 = require("motion/react");
39
+ var import_lucide_react = require("lucide-react");
40
+ var import_jsx_runtime = require("react/jsx-runtime");
41
+ function useTheme() {
42
+ const [resolvedTheme, setResolvedTheme] = (0, import_react.useState)("light");
43
+ (0, import_react.useEffect)(() => {
44
+ const leer = () => setResolvedTheme(
45
+ document.documentElement.classList.contains("dark") || !document.documentElement.classList.contains("light") && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
46
+ );
47
+ leer();
48
+ const obs = new MutationObserver(leer);
49
+ obs.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
50
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
51
+ mq.addEventListener("change", leer);
52
+ return () => {
53
+ obs.disconnect();
54
+ mq.removeEventListener("change", leer);
55
+ };
56
+ }, []);
57
+ return { resolvedTheme };
58
+ }
59
+ var PUBLISH_FORMATS = [
60
+ { id: "text", label: "Texto", icon: import_lucide_react.FileText },
61
+ { id: "artefact", label: "Artefacto", icon: import_lucide_react.Sparkles },
62
+ { id: "video", label: "Video", icon: import_lucide_react.Video },
63
+ { id: "audio", label: "Audio", icon: import_lucide_react.Music },
64
+ { id: "images", label: "Im\xE1genes", icon: import_lucide_react.Image }
65
+ ];
66
+ var MODELS = [
67
+ { id: "gaia7", label: "Gaia 7", note: "En desarrollo", disabled: true },
68
+ { id: "claude-opus-4-8", label: "G Claude Opus 4.8", note: null, disabled: false },
69
+ { id: "gemini", label: "G Gemini", note: "Sin cr\xE9ditos", disabled: true },
70
+ { id: "gpt", label: "G ChatGPT", note: "Sin cr\xE9ditos", disabled: true }
71
+ ];
72
+ function AutoTextarea({ value, onChange, onSubmit, onFocus, onBlur, placeholder }) {
73
+ const ref = (0, import_react.useRef)(null);
74
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
75
+ "textarea",
76
+ {
77
+ ref,
78
+ value,
79
+ onChange: (e) => {
80
+ onChange(e.target.value);
81
+ if (ref.current) {
82
+ ref.current.style.height = "auto";
83
+ ref.current.style.height = `${ref.current.scrollHeight}px`;
84
+ }
85
+ },
86
+ onKeyDown: (e) => {
87
+ if (e.key === "Enter" && !e.shiftKey) {
88
+ e.preventDefault();
89
+ onSubmit();
90
+ }
91
+ },
92
+ onFocus,
93
+ onBlur,
94
+ placeholder: placeholder ?? "Pregunta lo que quieras",
95
+ rows: 2,
96
+ className: "w-full resize-none bg-transparent outline-none text-[14px] text-black dark:text-white tracking-[-0.02em] leading-[1.55] placeholder:text-black/30 dark:placeholder:text-white/55 select-text overflow-y-auto max-h-[160px] [&::-webkit-scrollbar]:w-[5px] [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-black/15 dark:[&::-webkit-scrollbar-thumb]:bg-white/15 [&::-webkit-scrollbar-thumb]:rounded-full hover:[&::-webkit-scrollbar-thumb]:bg-black/30 dark:hover:[&::-webkit-scrollbar-thumb]:bg-white/30"
97
+ }
98
+ );
99
+ }
100
+ var BLOBS = [
101
+ { bx: 0.15, by: 0.5, rx: 0.52, ry: 0.9, color: [202, 220, 252], speed: 1.1, phase: 0 },
102
+ { bx: 0.85, by: 0.5, rx: 0.48, ry: 0.85, color: [160, 200, 240], speed: 0.85, phase: 2.09 },
103
+ { bx: 0.5, by: 0.2, rx: 0.42, ry: 0.75, color: [190, 240, 225], speed: 0.95, phase: 4.18 },
104
+ { bx: 0.52, by: 0.8, rx: 0.38, ry: 0.7, color: [220, 200, 255], speed: 1.2, phase: 1.05 },
105
+ { bx: 0.7, by: 0.4, rx: 0.3, ry: 0.6, color: [255, 220, 230], speed: 0.75, phase: 3.3 }
106
+ ];
107
+ var DARK_BLOBS = [
108
+ { bx: 0.15, by: 0.5, rx: 0.52, ry: 0.9, color: [64, 130, 255], speed: 1.1, phase: 0 },
109
+ { bx: 0.85, by: 0.5, rx: 0.48, ry: 0.85, color: [40, 210, 235], speed: 0.85, phase: 2.09 },
110
+ { bx: 0.5, by: 0.2, rx: 0.42, ry: 0.75, color: [60, 235, 170], speed: 0.95, phase: 4.18 },
111
+ { bx: 0.52, by: 0.8, rx: 0.38, ry: 0.7, color: [175, 90, 255], speed: 1.2, phase: 1.05 },
112
+ { bx: 0.7, by: 0.4, rx: 0.3, ry: 0.6, color: [255, 80, 150], speed: 0.75, phase: 3.3 }
113
+ ];
114
+ function VoiceCanvas({ recording, voiceMode }) {
115
+ const canvasRef = (0, import_react.useRef)(null);
116
+ const recRef = (0, import_react.useRef)(recording);
117
+ recRef.current = recording;
118
+ const volRef = (0, import_react.useRef)(0);
119
+ const { resolvedTheme } = useTheme();
120
+ const darkRef = (0, import_react.useRef)(resolvedTheme === "dark");
121
+ (0, import_react.useEffect)(() => {
122
+ darkRef.current = resolvedTheme === "dark";
123
+ }, [resolvedTheme]);
124
+ (0, import_react.useEffect)(() => {
125
+ if (!voiceMode) {
126
+ volRef.current = 0;
127
+ return;
128
+ }
129
+ let actx;
130
+ let analyser;
131
+ let src;
132
+ let stream;
133
+ let raf;
134
+ const buf = new Uint8Array(1024);
135
+ const tick = () => {
136
+ analyser.getByteTimeDomainData(buf);
137
+ let sum = 0;
138
+ for (let i = 0; i < buf.length; i++) {
139
+ const v = ((buf[i] ?? 128) - 128) / 128;
140
+ sum += v * v;
141
+ }
142
+ const rms = Math.sqrt(sum / buf.length);
143
+ volRef.current += (Math.min(1, rms * 12) - volRef.current) * 0.18;
144
+ raf = requestAnimationFrame(tick);
145
+ };
146
+ navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((s) => {
147
+ stream = s;
148
+ actx = new AudioContext();
149
+ analyser = actx.createAnalyser();
150
+ analyser.fftSize = 2048;
151
+ analyser.smoothingTimeConstant = 0.8;
152
+ src = actx.createMediaStreamSource(stream);
153
+ src.connect(analyser);
154
+ raf = requestAnimationFrame(tick);
155
+ }).catch(() => {
156
+ });
157
+ return () => {
158
+ cancelAnimationFrame(raf);
159
+ src?.disconnect();
160
+ stream?.getTracks().forEach((t) => t.stop());
161
+ actx?.close().catch(() => {
162
+ });
163
+ volRef.current = 0;
164
+ };
165
+ }, [voiceMode]);
166
+ (0, import_react.useEffect)(() => {
167
+ const canvas = canvasRef.current;
168
+ if (!canvas) return;
169
+ const ctx = canvas.getContext("2d");
170
+ if (!ctx) return;
171
+ let raf;
172
+ let t = 0;
173
+ const resize = () => {
174
+ const dpr = window.devicePixelRatio || 1;
175
+ canvas.width = canvas.offsetWidth * dpr;
176
+ canvas.height = canvas.offsetHeight * dpr;
177
+ ctx.scale(dpr, dpr);
178
+ };
179
+ const render = () => {
180
+ const w = canvas.offsetWidth;
181
+ const h = canvas.offsetHeight;
182
+ ctx.clearRect(0, 0, w, h);
183
+ const vol = volRef.current;
184
+ const boost = 1 + vol * 0.4;
185
+ const palette = darkRef.current ? DARK_BLOBS : BLOBS;
186
+ for (const b of palette) {
187
+ const amp = 0.13 + vol * 0.07;
188
+ const x = (b.bx + amp * Math.sin(t * 9e-4 * b.speed + b.phase) + 0.06 * Math.cos(t * 13e-4 * b.speed + b.phase * 1.7)) * w;
189
+ const y = (b.by + (0.09 + vol * 0.04) * Math.cos(t * 8e-4 * b.speed + b.phase + 1) + 0.04 * Math.sin(t * 11e-4 * b.speed + b.phase * 0.9)) * h;
190
+ const rx = b.rx * w * 0.68 * boost;
191
+ const ry = b.ry * h * 1.6 * boost;
192
+ const r = Math.max(rx, ry);
193
+ const [r0, g0, b0] = b.color;
194
+ const a0 = Math.min(0.85, 0.55 + vol * 0.3);
195
+ const grad = ctx.createRadialGradient(x, y, 0, x, y, r);
196
+ grad.addColorStop(0, `rgba(${r0},${g0},${b0},${a0.toFixed(2)})`);
197
+ grad.addColorStop(0.5, `rgba(${r0},${g0},${b0},${(a0 * 0.42).toFixed(2)})`);
198
+ grad.addColorStop(1, `rgba(${r0},${g0},${b0},0)`);
199
+ ctx.fillStyle = grad;
200
+ ctx.beginPath();
201
+ ctx.ellipse(x, y, rx, ry, 0, 0, Math.PI * 2);
202
+ ctx.fill();
203
+ }
204
+ const lx = (0.3 + 0.07 * Math.sin(t * 6e-4)) * w;
205
+ const ly = (0.44 + 0.05 * Math.cos(t * 7e-4)) * h;
206
+ const lr = (0.26 + vol * 0.1) * w;
207
+ const lg = ctx.createRadialGradient(lx, ly, 0, lx, ly, lr);
208
+ if (darkRef.current) {
209
+ const peak = Math.min(0.75, 0.45 + vol * 0.3);
210
+ lg.addColorStop(0, `rgba(255,214,120,${peak.toFixed(2)})`);
211
+ lg.addColorStop(0.45, `rgba(255,190,90,${(peak * 0.5).toFixed(2)})`);
212
+ lg.addColorStop(1, "rgba(255,190,90,0)");
213
+ } else {
214
+ lg.addColorStop(0, `rgba(255,255,255,${Math.min(0.9, 0.6 + vol * 0.3).toFixed(2)})`);
215
+ lg.addColorStop(1, "rgba(255,255,255,0)");
216
+ }
217
+ ctx.fillStyle = lg;
218
+ ctx.beginPath();
219
+ ctx.arc(lx, ly, lr, 0, Math.PI * 2);
220
+ ctx.fill();
221
+ if (vol > 0.04 || recRef.current) {
222
+ const cx = w * 0.5, cy = h * 0.5;
223
+ const maxR = Math.hypot(w, h) * 0.55;
224
+ const spd = 25e-4 + vol * 6e-3;
225
+ for (let i = 0; i < 3; i++) {
226
+ const phase = (t * spd + i * 0.33) % 1;
227
+ const alpha = (1 - phase) * Math.min(0.55, 0.1 + vol * 0.55);
228
+ ctx.beginPath();
229
+ ctx.arc(cx, cy, phase * maxR, 0, Math.PI * 2);
230
+ ctx.strokeStyle = `rgba(180,170,255,${alpha.toFixed(3)})`;
231
+ ctx.lineWidth = 1 + vol * 2.5;
232
+ ctx.stroke();
233
+ }
234
+ }
235
+ t += 16;
236
+ raf = requestAnimationFrame(render);
237
+ };
238
+ resize();
239
+ render();
240
+ const ro = new ResizeObserver(resize);
241
+ ro.observe(canvas);
242
+ return () => {
243
+ cancelAnimationFrame(raf);
244
+ ro.disconnect();
245
+ };
246
+ }, []);
247
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
248
+ "canvas",
249
+ {
250
+ ref: canvasRef,
251
+ className: "absolute inset-0 w-full h-full pointer-events-none",
252
+ style: { borderRadius: "inherit" }
253
+ }
254
+ );
255
+ }
256
+ function InputBar({
257
+ value,
258
+ onChange,
259
+ onSend,
260
+ onFocus,
261
+ onBlur,
262
+ placeholder,
263
+ aiPhase = "idle",
264
+ aiStatus = "",
265
+ sentText = "",
266
+ thinkingLog = [],
267
+ thinkingOpen = false,
268
+ onThinkingToggle,
269
+ recording = false,
270
+ recordSecs = 0,
271
+ onStartRecording,
272
+ onCancelRecording,
273
+ onSendRecording,
274
+ voiceMode = false,
275
+ onVoiceModeToggle,
276
+ model,
277
+ onModelChange,
278
+ connectors = [],
279
+ onFileSelected,
280
+ onCloudOpen,
281
+ onConnectorInsert,
282
+ onNavigateConnectors,
283
+ enablePublish = false,
284
+ publishMode = false,
285
+ onPublishModeChange,
286
+ pubTitle = "",
287
+ onPubTitleChange,
288
+ pubBody = "",
289
+ onPubBodyChange,
290
+ pubFormat = "text",
291
+ onPubFormatChange,
292
+ onPublish,
293
+ onPubFileSelected,
294
+ pubFileName = null,
295
+ pubUploading = false,
296
+ actionsEndSlot
297
+ }) {
298
+ const [attachOpen, setAttachOpen] = (0, import_react.useState)(false);
299
+ const [modelOpen, setModelOpen] = (0, import_react.useState)(false);
300
+ const fileRef = (0, import_react.useRef)(null);
301
+ const camRef = (0, import_react.useRef)(null);
302
+ const pubFileRef = (0, import_react.useRef)(null);
303
+ const handleFileChange = (e) => {
304
+ const file = e.target.files?.[0];
305
+ if (!file) return;
306
+ onFileSelected?.(file);
307
+ setAttachOpen(false);
308
+ e.target.value = "";
309
+ };
310
+ const connectedList = connectors.filter((c) => c.status === "connected").slice(0, 4);
311
+ const hasMoreConn = connectors.some((c) => c.status !== "connected");
312
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col", children: [
313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { children: thinkingOpen && aiPhase !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
314
+ import_react2.motion.div,
315
+ {
316
+ initial: { opacity: 0, height: 0, y: 8 },
317
+ animate: { opacity: 1, height: "auto", y: 0 },
318
+ exit: { opacity: 0, height: 0, y: 8 },
319
+ transition: { type: "spring", stiffness: 340, damping: 32 },
320
+ className: "mb-2 overflow-hidden rounded-2xl border border-black/[0.08] dark:border-white/[0.13] bg-white/90 dark:bg-[#171717]/90 backdrop-blur-sm shadow-[0_8px_32px_-12px_rgba(0,0,0,0.18)] dark:shadow-[0_8px_32px_-12px_rgba(0,0,0,0.41)]",
321
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-4 pt-3 pb-3 flex flex-col gap-0", children: [
322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
323
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[10px] font-semibold tracking-[0.08em] uppercase text-black/30 dark:text-white/55", children: "Proceso interno" }),
324
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
325
+ "button",
326
+ {
327
+ onClick: onThinkingToggle,
328
+ className: "w-5 h-5 flex items-center justify-center rounded-full text-black/30 dark:text-white/55 hover:text-black/60 dark:hover:text-white/77 hover:bg-black/[0.05] dark:hover:bg-white/[0.05] transition-colors",
329
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "w-3 h-3", strokeWidth: 2 })
330
+ }
331
+ )
332
+ ] }),
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1.5 max-h-[180px] overflow-y-auto [&::-webkit-scrollbar]:w-[3px] [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-black/10 dark:[&::-webkit-scrollbar-thumb]:bg-white/10 [&::-webkit-scrollbar-thumb]:rounded-full", children: [
334
+ thinkingLog.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
335
+ import_react2.motion.div,
336
+ {
337
+ initial: { opacity: 0, x: -4 },
338
+ animate: { opacity: 1, x: 0 },
339
+ transition: { duration: 0.2 },
340
+ className: "flex items-start gap-2",
341
+ children: [
342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `mt-[5px] w-[5px] h-[5px] rounded-full shrink-0 ${line.startsWith("\u2713") ? "bg-black/50 dark:bg-white/50" : "bg-black/15 dark:bg-white/15"}` }),
343
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `text-[12px] tracking-[-0.01em] leading-[1.5] ${line.startsWith("\u2713") ? "text-black/60 dark:text-white/77" : "text-black/35 dark:text-white/55"}`, children: line })
344
+ ]
345
+ },
346
+ i
347
+ )),
348
+ (aiPhase === "thinking" || aiPhase === "acting") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-center gap-2 pl-[13px]", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
349
+ import_react2.motion.span,
350
+ {
351
+ className: "w-[3px] h-[3px] rounded-full bg-black/20 dark:bg-white/20 block",
352
+ animate: { opacity: [0.2, 0.8, 0.2] },
353
+ transition: { duration: 1, repeat: Infinity, delay: i * 0.18 }
354
+ },
355
+ i
356
+ )) })
357
+ ] })
358
+ ] })
359
+ }
360
+ ) }),
361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
362
+ import_react2.motion.div,
363
+ {
364
+ className: "flex flex-col rounded-2xl bg-white dark:bg-[#171717] relative",
365
+ style: { borderWidth: "1px", borderStyle: "solid" },
366
+ animate: voiceMode ? {
367
+ borderColor: [
368
+ "rgba(167,139,250,0.55)",
369
+ "rgba(96,165,250,0.55)",
370
+ "rgba(34,211,238,0.55)",
371
+ "rgba(192,132,252,0.55)",
372
+ "rgba(167,139,250,0.55)"
373
+ ],
374
+ boxShadow: [
375
+ "0 0 0 3px rgba(139,92,246,0.10), 0 8px 40px -8px rgba(139,92,246,0.28)",
376
+ "0 0 0 3px rgba(59,130,246,0.10), 0 8px 40px -8px rgba(59,130,246,0.22)",
377
+ "0 0 0 3px rgba(6,182,212,0.10), 0 8px 40px -8px rgba(6,182,212,0.28)",
378
+ "0 0 0 3px rgba(168,85,247,0.10), 0 8px 40px -8px rgba(168,85,247,0.28)",
379
+ "0 0 0 3px rgba(139,92,246,0.10), 0 8px 40px -8px rgba(139,92,246,0.28)"
380
+ ]
381
+ } : {
382
+ borderColor: "var(--field-border)",
383
+ boxShadow: "0 1px 3px rgba(0,0,0,0.05), 0 12px 40px -20px rgba(0,0,0,0.12)"
384
+ },
385
+ transition: voiceMode ? {
386
+ borderColor: { repeat: Infinity, duration: 4, ease: "easeInOut" },
387
+ boxShadow: { repeat: Infinity, duration: 4, ease: "easeInOut" }
388
+ } : { duration: 0.5 },
389
+ children: [
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: fileRef, type: "file", className: "hidden", onChange: handleFileChange }),
391
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: camRef, type: "file", accept: "image/*,video/*", className: "hidden", onChange: handleFileChange }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ref: pubFileRef, type: "file", className: "hidden", onChange: (e) => {
393
+ const f = e.target.files?.[0];
394
+ if (f) onPubFileSelected?.(f);
395
+ e.target.value = "";
396
+ } }),
397
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { mode: "wait", initial: false, children: publishMode ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
398
+ import_react2.motion.div,
399
+ {
400
+ initial: { opacity: 0 },
401
+ animate: { opacity: 1 },
402
+ exit: { opacity: 0 },
403
+ transition: { duration: 0.15 },
404
+ children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-4 pt-3 pb-1", children: [
406
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[10px] tracking-[0.08em] uppercase text-black/30 dark:text-white/55", children: "Nueva publicaci\xF3n" }),
407
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
408
+ "button",
409
+ {
410
+ onClick: () => onPublishModeChange?.(false),
411
+ className: "text-black/30 hover:text-black/60 dark:hover:text-white/77 transition-colors",
412
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "w-3.5 h-3.5", strokeWidth: 1.8 })
413
+ }
414
+ )
415
+ ] }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pb-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
417
+ "input",
418
+ {
419
+ autoFocus: true,
420
+ value: pubTitle,
421
+ onChange: (e) => onPubTitleChange?.(e.target.value),
422
+ placeholder: "T\xEDtulo\u2026",
423
+ className: "w-full text-[14px] font-medium text-black dark:text-white tracking-[-0.025em] placeholder:text-black/25 dark:placeholder:text-white/55 bg-transparent outline-none"
424
+ }
425
+ ) }),
426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pt-1.5 pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
427
+ "textarea",
428
+ {
429
+ value: pubBody,
430
+ onChange: (e) => onPubBodyChange?.(e.target.value),
431
+ placeholder: "Descripci\xF3n\u2026 usa # para temas y @ para mencionar personas",
432
+ rows: 2,
433
+ className: "w-full resize-none text-[12.5px] text-black/60 dark:text-white/77 tracking-[-0.01em] leading-relaxed placeholder:text-black/25 dark:placeholder:text-white/55 bg-transparent outline-none"
434
+ }
435
+ ) }),
436
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pb-2.5 flex items-center gap-1.5 overflow-x-auto no-scrollbar", children: PUBLISH_FORMATS.map((f) => {
437
+ const Ico = f.icon;
438
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
439
+ "button",
440
+ {
441
+ onClick: () => onPubFormatChange?.(f.id),
442
+ className: `shrink-0 h-6 pl-1.5 pr-2.5 rounded-full inline-flex items-center gap-1 text-[10.5px] tracking-[-0.01em] transition-all ${pubFormat === f.id ? "bg-black dark:bg-white text-white dark:text-black" : "bg-black/[0.04] dark:bg-white/[0.04] text-black/45 dark:text-white/63 hover:bg-black/[0.07] dark:hover:bg-white/[0.07] hover:text-black dark:hover:text-white"}`,
443
+ children: [
444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Ico, { className: "w-2.5 h-2.5", strokeWidth: 1.7 }),
445
+ f.label
446
+ ]
447
+ },
448
+ f.id
449
+ );
450
+ }) }),
451
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1.5 px-3 pb-3 border-t border-black/[0.05] dark:border-white/[0.11] pt-2", children: [
452
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
453
+ "button",
454
+ {
455
+ onClick: () => pubFileRef.current?.click(),
456
+ disabled: pubUploading,
457
+ className: "w-7 h-7 shrink-0 rounded-full flex items-center justify-center text-black/40 dark:text-white/55 hover:text-black dark:hover:text-white hover:bg-black/[0.05] dark:hover:bg-white/[0.05] transition-colors disabled:opacity-40",
458
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plus, { className: "w-3.5 h-3.5", strokeWidth: 1.7 })
459
+ }
460
+ ),
461
+ (pubUploading || pubFileName) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[10.5px] text-black/40 dark:text-white/55 tracking-[-0.01em] truncate max-w-[140px]", children: pubUploading ? "Subiendo\u2026" : pubFileName }),
462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" }),
463
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
464
+ "button",
465
+ {
466
+ onClick: () => onPublishModeChange?.(false),
467
+ className: "h-7 px-3.5 rounded-full text-[11.5px] text-black/40 dark:text-white/55 hover:text-black dark:hover:text-white hover:bg-black/[0.04] dark:hover:bg-white/[0.04] tracking-[-0.01em] transition-colors",
468
+ children: "Cancelar"
469
+ }
470
+ ),
471
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
472
+ "button",
473
+ {
474
+ onClick: onPublish,
475
+ disabled: !pubTitle.trim() || pubUploading,
476
+ className: "h-7 px-4 rounded-full bg-black dark:bg-white text-white dark:text-black text-[11.5px] tracking-[-0.01em] hover:bg-black/85 dark:hover:bg-white/85 transition-colors disabled:opacity-30 disabled:cursor-not-allowed",
477
+ children: "Publicar"
478
+ }
479
+ )
480
+ ] })
481
+ ]
482
+ },
483
+ "publish"
484
+ ) : (
485
+ /* ── NORMAL / RECORDING / AI PHASE ──────────────── */
486
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
487
+ import_react2.motion.div,
488
+ {
489
+ initial: { opacity: 0 },
490
+ animate: { opacity: 1 },
491
+ exit: { opacity: 0 },
492
+ transition: { duration: 0.15 },
493
+ children: [
494
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pt-3 pb-0 min-h-[52px] flex items-start", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { mode: "wait", children: recording ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
495
+ import_react2.motion.div,
496
+ {
497
+ initial: { opacity: 0, y: 4 },
498
+ animate: { opacity: 1, y: 0 },
499
+ exit: { opacity: 0, y: 4 },
500
+ transition: { duration: 0.18 },
501
+ className: "w-full py-[7px] flex items-center gap-3",
502
+ children: [
503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative shrink-0", children: [
504
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
505
+ import_react2.motion.span,
506
+ {
507
+ className: "absolute inset-0 rounded-full bg-black/10 dark:bg-white/10",
508
+ animate: { scale: [1, 1.7, 1], opacity: [0.5, 0, 0.5] },
509
+ transition: { duration: 1.4, repeat: Infinity, ease: "easeInOut" }
510
+ }
511
+ ),
512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "w-[9px] h-[9px] rounded-full bg-black dark:bg-white relative" })
513
+ ] }),
514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-center gap-[3px]", children: [0.6, 1, 0.7, 1.2, 0.5, 0.9, 1.1, 0.6, 0.8, 1, 0.7, 0.5].map((h, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
515
+ import_react2.motion.span,
516
+ {
517
+ className: "w-[2.5px] rounded-full bg-black/50 dark:bg-white/50 block",
518
+ animate: { scaleY: [h * 0.4, h, h * 0.3, h * 0.9, h * 0.5] },
519
+ transition: { duration: 0.8 + i * 0.07, repeat: Infinity, repeatType: "mirror", ease: "easeInOut" },
520
+ style: { height: 18, transformOrigin: "center" }
521
+ },
522
+ i
523
+ )) }),
524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-[13px] tracking-[-0.01em] text-black/45 dark:text-white/63 tabular-nums", children: [
525
+ String(Math.floor(recordSecs / 60)).padStart(2, "0"),
526
+ ":",
527
+ String(recordSecs % 60).padStart(2, "0")
528
+ ] })
529
+ ]
530
+ },
531
+ "recording"
532
+ ) : aiPhase === "idle" ? (
533
+ /* Normal text input */
534
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
535
+ import_react2.motion.div,
536
+ {
537
+ initial: { opacity: 0 },
538
+ animate: { opacity: 1 },
539
+ exit: { opacity: 0 },
540
+ transition: { duration: 0.15 },
541
+ className: "w-full",
542
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
543
+ AutoTextarea,
544
+ {
545
+ value,
546
+ onChange,
547
+ onSubmit: onSend,
548
+ onFocus,
549
+ onBlur,
550
+ placeholder
551
+ }
552
+ )
553
+ },
554
+ "textarea"
555
+ )
556
+ ) : (
557
+ /* AI phase display */
558
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
559
+ import_react2.motion.div,
560
+ {
561
+ initial: { opacity: 0, y: 4 },
562
+ animate: { opacity: 1, y: 0 },
563
+ exit: { opacity: 0 },
564
+ transition: { duration: 0.2 },
565
+ className: "w-full py-[7px] flex items-center justify-between gap-3",
566
+ children: [
567
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react2.AnimatePresence, { mode: "wait", children: [
568
+ aiPhase === "sent" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
569
+ import_react2.motion.p,
570
+ {
571
+ initial: { opacity: 0, y: 3 },
572
+ animate: { opacity: 1, y: 0 },
573
+ exit: { opacity: 0, y: -3 },
574
+ transition: { duration: 0.18 },
575
+ className: "text-[14px] tracking-[-0.02em] leading-[1.55] text-black/80 dark:text-white/92 truncate flex-1",
576
+ children: sentText
577
+ },
578
+ "sent"
579
+ ),
580
+ aiPhase === "thinking" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
581
+ import_react2.motion.div,
582
+ {
583
+ initial: { opacity: 0, y: 3 },
584
+ animate: { opacity: 1, y: 0 },
585
+ exit: { opacity: 0, y: -3 },
586
+ transition: { duration: 0.18 },
587
+ className: "flex items-center gap-2 flex-1",
588
+ children: [
589
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[13.5px] tracking-[-0.02em] text-black/40 dark:text-white/55", children: "Pensando" }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex gap-[3px] items-center", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
591
+ import_react2.motion.span,
592
+ {
593
+ className: "w-[4px] h-[4px] rounded-full bg-black/30 dark:bg-white/30 block",
594
+ animate: { opacity: [0.3, 1, 0.3] },
595
+ transition: { duration: 1.2, repeat: Infinity, delay: i * 0.2 }
596
+ },
597
+ i
598
+ )) })
599
+ ]
600
+ },
601
+ "thinking"
602
+ ),
603
+ aiPhase === "acting" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
604
+ import_react2.motion.p,
605
+ {
606
+ initial: { opacity: 0, y: 3 },
607
+ animate: { opacity: 1, y: 0 },
608
+ exit: { opacity: 0, y: -3 },
609
+ transition: { duration: 0.2 },
610
+ className: "text-[13.5px] tracking-[-0.02em] text-black/40 dark:text-white/55 flex-1",
611
+ children: aiStatus
612
+ },
613
+ aiStatus
614
+ ),
615
+ aiPhase === "done" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
616
+ import_react2.motion.p,
617
+ {
618
+ initial: { opacity: 0, y: 3 },
619
+ animate: { opacity: 1, y: 0 },
620
+ exit: { opacity: 0, y: -3 },
621
+ transition: { duration: 0.18 },
622
+ className: "text-[13.5px] tracking-[-0.02em] text-black/40 dark:text-white/55 flex-1",
623
+ children: "Listo"
624
+ },
625
+ "done"
626
+ )
627
+ ] }),
628
+ aiPhase !== "sent" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
629
+ "button",
630
+ {
631
+ onClick: onThinkingToggle,
632
+ className: `shrink-0 h-6 px-2.5 rounded-full border text-[11px] tracking-[-0.01em] transition-colors ${thinkingOpen ? "border-black/20 dark:border-white/26 bg-black/[0.05] dark:bg-white/[0.05] text-black/60 dark:text-white/77" : "border-black/[0.08] dark:border-white/[0.13] text-black/30 dark:text-white/55 hover:text-black/55 dark:hover:text-white/69 hover:border-black/15 dark:hover:border-white/21"}`,
633
+ children: thinkingOpen ? "Cerrar" : "Ver m\xE1s"
634
+ }
635
+ )
636
+ ]
637
+ },
638
+ "status"
639
+ )
640
+ ) }) }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-1.5 px-3 pt-1 pb-2.5", children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative shrink-0", children: [
643
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
644
+ "button",
645
+ {
646
+ onClick: () => setAttachOpen((o) => !o),
647
+ className: `w-8 h-8 shrink-0 rounded-full flex items-center justify-center transition-colors ${attachOpen ? "bg-black/[0.07] dark:bg-white/[0.07] text-black dark:text-white" : "text-black/70 dark:text-white/87 hover:text-black dark:hover:text-white hover:bg-black/[0.05] dark:hover:bg-white/[0.05]"}`,
648
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plus, { className: `w-[15px] h-[15px] transition-transform duration-200 ${attachOpen ? "rotate-45" : ""}` })
649
+ }
650
+ ),
651
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { children: attachOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
652
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
653
+ import_react2.motion.div,
654
+ {
655
+ initial: { opacity: 0 },
656
+ animate: { opacity: 1 },
657
+ exit: { opacity: 0 },
658
+ className: "fixed inset-0 z-30 bg-black/20 backdrop-blur-[2px]",
659
+ onClick: () => setAttachOpen(false)
660
+ }
661
+ ),
662
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
663
+ import_react2.motion.div,
664
+ {
665
+ initial: { opacity: 0, scale: 0.96, y: 8 },
666
+ animate: { opacity: 1, scale: 1, y: 0 },
667
+ exit: { opacity: 0, scale: 0.97, y: 6 },
668
+ transition: { type: "spring", stiffness: 340, damping: 30 },
669
+ className: "hidden md:flex fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[340px] max-w-[90vw] flex-col rounded-2xl border border-black/[0.09] dark:border-white/[0.14] bg-white/90 dark:bg-[#171717]/90 backdrop-blur-xl shadow-[0_30px_90px_-20px_rgba(0,0,0,0.28)] dark:shadow-[0_30px_90px_-20px_rgba(0,0,0,0.64)] z-40 overflow-hidden",
670
+ children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between px-4 pt-4 pb-3 border-b border-black/[0.06] dark:border-white/[0.11]", children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[13px] font-medium tracking-[-0.02em] text-black dark:text-white", children: "Adjuntar" }),
673
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
674
+ "button",
675
+ {
676
+ onClick: () => setAttachOpen(false),
677
+ className: "w-7 h-7 flex items-center justify-center rounded-full text-black/35 dark:text-white/55 hover:text-black dark:hover:text-white hover:bg-black/[0.05] dark:hover:bg-white/[0.05] transition-colors",
678
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "w-4 h-4", strokeWidth: 1.8 })
679
+ }
680
+ )
681
+ ] }),
682
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-2 flex flex-col gap-0.5", children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
684
+ "button",
685
+ {
686
+ onClick: () => {
687
+ setAttachOpen(false);
688
+ fileRef.current?.click();
689
+ },
690
+ className: "flex items-center gap-3 h-10 px-3 rounded-xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group",
691
+ children: [
692
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-7 h-7 rounded-xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.HardDrive, { className: "w-4 h-4 text-black/55 dark:text-white/69", strokeWidth: 1.6 }) }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col", children: [
694
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[12.5px] tracking-[-0.02em] text-black/80 dark:text-white/92 leading-tight", children: "Desde dispositivo" }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] text-black/30 dark:text-white/55 tracking-[-0.01em]", children: "Cualquier archivo local" })
696
+ ] })
697
+ ]
698
+ }
699
+ ),
700
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
701
+ "button",
702
+ {
703
+ onClick: () => {
704
+ setAttachOpen(false);
705
+ camRef.current?.click();
706
+ },
707
+ className: "flex items-center gap-3 h-10 px-3 rounded-xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group",
708
+ children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-7 h-7 rounded-xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Camera, { className: "w-4 h-4 text-black/55 dark:text-white/69", strokeWidth: 1.6 }) }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col", children: [
711
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[12.5px] tracking-[-0.02em] text-black/80 dark:text-white/92 leading-tight", children: "C\xE1mara / imagen" }),
712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] text-black/30 dark:text-white/55 tracking-[-0.01em]", children: "Foto o video" })
713
+ ] })
714
+ ]
715
+ }
716
+ ),
717
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
718
+ "button",
719
+ {
720
+ onClick: () => {
721
+ setAttachOpen(false);
722
+ onCloudOpen?.();
723
+ },
724
+ className: "flex items-center gap-3 h-10 px-3 rounded-xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group",
725
+ children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-7 h-7 rounded-xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Cloud, { className: "w-4 h-4 text-black/55 dark:text-white/69", strokeWidth: 1.6 }) }),
727
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col", children: [
728
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[12.5px] tracking-[-0.02em] text-black/80 dark:text-white/92 leading-tight", children: "Nube de Handeia" }),
729
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] text-black/30 dark:text-white/55 tracking-[-0.01em]", children: "Tus archivos guardados" })
730
+ ] })
731
+ ]
732
+ }
733
+ ),
734
+ connectedList.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-3 my-1.5 border-t border-black/[0.06] dark:border-white/[0.11]" }),
736
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "px-3 pb-1 text-[9.5px] tracking-[0.1em] uppercase text-black/25 dark:text-white/55", children: "Conectores" }),
737
+ connectedList.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
738
+ "button",
739
+ {
740
+ onClick: () => {
741
+ onConnectorInsert?.(c.name);
742
+ setAttachOpen(false);
743
+ },
744
+ className: "flex items-center gap-3 h-10 px-3 rounded-xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group",
745
+ children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-7 h-7 rounded-xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Link, { className: "w-3.5 h-3.5 text-black/50 dark:text-white/66", strokeWidth: 1.6 }) }),
747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col flex-1 min-w-0", children: [
748
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[12.5px] tracking-[-0.02em] text-black/80 dark:text-white/92 leading-tight truncate", children: c.name }),
749
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] text-black/30 dark:text-white/55 tracking-[-0.01em]", children: "Conectado" })
750
+ ] })
751
+ ]
752
+ },
753
+ c.id
754
+ ))
755
+ ] }),
756
+ hasMoreConn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
757
+ "button",
758
+ {
759
+ onClick: () => {
760
+ setAttachOpen(false);
761
+ onNavigateConnectors?.();
762
+ },
763
+ className: "flex items-center gap-3 h-10 px-3 rounded-xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group",
764
+ children: [
765
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-7 h-7 rounded-xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plug, { className: "w-3.5 h-3.5 text-black/35 dark:text-white/55", strokeWidth: 1.6 }) }),
766
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[12.5px] tracking-[-0.02em] text-black/40 dark:text-white/55", children: "Ver todos los conectores" })
767
+ ]
768
+ }
769
+ )
770
+ ] })
771
+ ]
772
+ }
773
+ ),
774
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
775
+ import_react2.motion.div,
776
+ {
777
+ initial: { y: "100%" },
778
+ animate: { y: 0 },
779
+ exit: { y: "100%" },
780
+ transition: { type: "spring", stiffness: 380, damping: 36 },
781
+ className: "md:hidden fixed inset-x-0 bottom-0 z-40 flex flex-col rounded-t-3xl border-t border-black/[0.08] dark:border-white/[0.13] bg-white/90 dark:bg-[#171717]/90 backdrop-blur-xl shadow-[0_-16px_48px_-12px_rgba(0,0,0,0.2)] dark:shadow-[0_-16px_48px_-12px_rgba(0,0,0,0.46)]",
782
+ style: { maxHeight: "52vh" },
783
+ children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pt-3 pb-1 flex justify-center shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-9 h-1 rounded-full bg-black/15 dark:bg-white/15" }) }),
785
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "overflow-y-auto px-3 pb-8 flex flex-col gap-0.5", children: [
786
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => {
787
+ setAttachOpen(false);
788
+ fileRef.current?.click();
789
+ }, className: "flex items-center gap-3 h-12 px-3 rounded-2xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group", children: [
790
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-9 h-9 rounded-2xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.HardDrive, { className: "w-[18px] h-[18px] text-black/55 dark:text-white/69", strokeWidth: 1.6 }) }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[13.5px] tracking-[-0.02em] text-black/80 dark:text-white/92", children: "Desde dispositivo" }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[11.5px] text-black/30 dark:text-white/55", children: "Cualquier archivo local" })
794
+ ] })
795
+ ] }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => {
797
+ setAttachOpen(false);
798
+ camRef.current?.click();
799
+ }, className: "flex items-center gap-3 h-12 px-3 rounded-2xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group", children: [
800
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-9 h-9 rounded-2xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Camera, { className: "w-[18px] h-[18px] text-black/55 dark:text-white/69", strokeWidth: 1.6 }) }),
801
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
802
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[13.5px] tracking-[-0.02em] text-black/80 dark:text-white/92", children: "C\xE1mara / imagen" }),
803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[11.5px] text-black/30 dark:text-white/55", children: "Foto o video" })
804
+ ] })
805
+ ] }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => {
807
+ setAttachOpen(false);
808
+ onCloudOpen?.();
809
+ }, className: "flex items-center gap-3 h-12 px-3 rounded-2xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group", children: [
810
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-9 h-9 rounded-2xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Cloud, { className: "w-[18px] h-[18px] text-black/55 dark:text-white/69", strokeWidth: 1.6 }) }),
811
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[13.5px] tracking-[-0.02em] text-black/80 dark:text-white/92", children: "Nube de Handeia" }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[11.5px] text-black/30 dark:text-white/55", children: "Tus archivos guardados" })
814
+ ] })
815
+ ] }),
816
+ connectedList.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
817
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-3 my-2 border-t border-black/[0.06] dark:border-white/[0.11]" }),
818
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "px-3 pb-1 text-[10px] tracking-[0.1em] uppercase text-black/25 dark:text-white/55", children: "Conectores" }),
819
+ connectedList.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => {
820
+ onConnectorInsert?.(c.name);
821
+ setAttachOpen(false);
822
+ }, className: "flex items-center gap-3 h-12 px-3 rounded-2xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group", children: [
823
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-9 h-9 rounded-2xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Link, { className: "w-[16px] h-[16px] text-black/50 dark:text-white/66", strokeWidth: 1.6 }) }),
824
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
825
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[13.5px] tracking-[-0.02em] text-black/80 dark:text-white/92", children: c.name }),
826
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-[11.5px] text-black/30 dark:text-white/55", children: "Conectado" })
827
+ ] })
828
+ ] }, c.id))
829
+ ] }),
830
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { onClick: () => {
831
+ setAttachOpen(false);
832
+ onNavigateConnectors?.();
833
+ }, className: "flex items-center gap-3 h-12 px-3 rounded-2xl text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.04] transition-colors group", children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "w-9 h-9 rounded-2xl bg-black/[0.05] dark:bg-white/[0.05] group-hover:bg-black/[0.07] flex items-center justify-center shrink-0 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plug, { className: "w-[16px] h-[16px] text-black/35 dark:text-white/55", strokeWidth: 1.6 }) }),
835
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[13.5px] tracking-[-0.02em] text-black/40 dark:text-white/55", children: "Ver todos los conectores" })
836
+ ] })
837
+ ] })
838
+ ]
839
+ }
840
+ )
841
+ ] }) })
842
+ ] }),
843
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative shrink-0", children: [
844
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
845
+ "button",
846
+ {
847
+ onClick: () => setModelOpen((o) => !o),
848
+ className: "h-7 inline-flex items-center gap-1.5 rounded-full border border-black/[0.09] dark:border-white/[0.14] px-3 text-[12px] tracking-[-0.02em] text-black/60 dark:text-white/77 hover:text-black dark:hover:text-white hover:border-black/20 dark:hover:border-white/26 hover:bg-black/[0.03] dark:hover:bg-white/[0.03] transition-colors",
849
+ children: [
850
+ MODELS.find((m) => m.id === model)?.label ?? model,
851
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ChevronDown, { className: `w-[11px] h-[11px] opacity-70 transition-transform ${modelOpen ? "rotate-180" : ""}` })
852
+ ]
853
+ }
854
+ ),
855
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { children: modelOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "fixed inset-0 z-30", onClick: () => setModelOpen(false) }),
857
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
858
+ import_react2.motion.div,
859
+ {
860
+ initial: { opacity: 0, y: 6, scale: 0.97 },
861
+ animate: { opacity: 1, y: 0, scale: 1 },
862
+ exit: { opacity: 0, y: 6, scale: 0.98 },
863
+ transition: { duration: 0.15 },
864
+ style: { transformOrigin: "bottom left" },
865
+ className: "absolute bottom-full left-0 mb-2 w-48 bg-white dark:bg-[#171717] rounded-xl border border-black/[0.09] dark:border-white/[0.14] shadow-[0_16px_36px_-12px_rgba(0,0,0,0.22)] dark:shadow-[0_16px_36px_-12px_rgba(0,0,0,0.51)] p-1 z-40",
866
+ children: MODELS.map((m) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
867
+ "button",
868
+ {
869
+ disabled: m.disabled,
870
+ onClick: () => {
871
+ if (m.disabled) return;
872
+ onModelChange(m.id);
873
+ setModelOpen(false);
874
+ },
875
+ className: `w-full flex items-center gap-2 h-8 px-2.5 rounded-lg text-[12.5px] tracking-[-0.02em] transition-colors ${m.disabled ? "text-black/25 dark:text-white/55 cursor-not-allowed" : model === m.id ? "bg-black/[0.05] dark:bg-white/[0.05] text-black dark:text-white" : "text-black/55 dark:text-white/69 hover:text-black dark:hover:text-white hover:bg-black/[0.03] dark:hover:bg-white/[0.03]"}`,
876
+ children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex-1 text-left", children: m.label }),
878
+ m.note && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[9.5px] tracking-[0.03em] uppercase text-black/30 dark:text-white/55", children: m.note }),
879
+ !m.disabled && model === m.id && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Check, { className: "w-3.5 h-3.5 shrink-0", strokeWidth: 2.4 })
880
+ ]
881
+ },
882
+ m.id
883
+ ))
884
+ }
885
+ )
886
+ ] }) })
887
+ ] }),
888
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1" }),
889
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { mode: "wait", children: recording ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
890
+ import_react2.motion.div,
891
+ {
892
+ initial: { opacity: 0, x: 8 },
893
+ animate: { opacity: 1, x: 0 },
894
+ exit: { opacity: 0, x: 8 },
895
+ transition: { duration: 0.15 },
896
+ className: "flex items-center gap-1",
897
+ children: [
898
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
899
+ "button",
900
+ {
901
+ onClick: onCancelRecording,
902
+ className: "h-7 px-3 rounded-full text-[11.5px] tracking-[-0.01em] text-black/40 dark:text-white/55 hover:text-black dark:hover:text-white hover:bg-black/[0.05] dark:hover:bg-white/[0.05] transition-colors border border-black/[0.08] dark:border-white/[0.13]",
903
+ children: "Cancelar"
904
+ }
905
+ ),
906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
907
+ "button",
908
+ {
909
+ onClick: onSendRecording,
910
+ className: "h-7 px-3 rounded-full text-[11.5px] tracking-[-0.01em] bg-black dark:bg-white text-white dark:text-black hover:bg-black/80 dark:hover:bg-white/80 transition-colors",
911
+ children: "Enviar"
912
+ }
913
+ )
914
+ ]
915
+ },
916
+ "rec-actions"
917
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
918
+ import_react2.motion.div,
919
+ {
920
+ initial: { opacity: 0 },
921
+ animate: { opacity: 1 },
922
+ exit: { opacity: 0 },
923
+ transition: { duration: 0.1 },
924
+ className: "flex items-center gap-1",
925
+ children: [
926
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
927
+ "button",
928
+ {
929
+ onClick: onStartRecording,
930
+ className: "w-8 h-8 shrink-0 rounded-full flex items-center justify-center text-black/70 dark:text-white/87 hover:text-black dark:hover:text-white hover:bg-black/[0.05] dark:hover:bg-white/[0.05] transition-colors",
931
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Mic, { className: "w-[15px] h-[15px]", strokeWidth: 1.7 })
932
+ }
933
+ ),
934
+ onVoiceModeToggle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
935
+ "button",
936
+ {
937
+ onClick: onVoiceModeToggle,
938
+ className: `w-8 h-8 shrink-0 rounded-full flex items-center justify-center transition-colors ${voiceMode ? "bg-black/[0.07] dark:bg-white/[0.07] text-black dark:text-white" : "text-black/70 dark:text-white/87 hover:text-black dark:hover:text-white hover:bg-black/[0.05] dark:hover:bg-white/[0.05]"}`,
939
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { mode: "wait", children: voiceMode ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.motion.span, { initial: { opacity: 0, rotate: -45 }, animate: { opacity: 1, rotate: 0 }, exit: { opacity: 0, rotate: 45 }, transition: { duration: 0.15 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "w-[15px] h-[15px]", strokeWidth: 1.8 }) }, "x") : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AudioWaveform, { className: "w-[15px] h-[15px]", strokeWidth: 1.7 }) }, "wave") })
940
+ }
941
+ ),
942
+ actionsEndSlot
943
+ ]
944
+ },
945
+ "mic-btn"
946
+ ) })
947
+ ] })
948
+ ]
949
+ },
950
+ "chat"
951
+ )
952
+ ) }),
953
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react2.AnimatePresence, { children: voiceMode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
954
+ import_react2.motion.div,
955
+ {
956
+ initial: { opacity: 0 },
957
+ animate: { opacity: 1 },
958
+ exit: { opacity: 0 },
959
+ transition: { duration: 0.8, ease: [0.16, 1, 0.3, 1] },
960
+ className: "absolute inset-0 pointer-events-none rounded-2xl overflow-hidden",
961
+ children: [
962
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VoiceCanvas, { recording, voiceMode }),
963
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
964
+ "div",
965
+ {
966
+ className: "absolute inset-0 mix-blend-overlay pointer-events-none",
967
+ style: {
968
+ opacity: 0.06,
969
+ backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")`,
970
+ backgroundSize: "160px 160px"
971
+ }
972
+ }
973
+ )
974
+ ]
975
+ }
976
+ ) })
977
+ ]
978
+ }
979
+ )
980
+ ] });
981
+ }
982
+
983
+ // src/agent.ts
984
+ var AGENT_PROTOCOL_VERSION = 1;
985
+
986
+ // src/react/agent.tsx
987
+ var import_jsx_runtime2 = require("react/jsx-runtime");
988
+ var CIRCLE_SIZE = 44;
989
+ var FIELD_GAP = 12;
990
+ var HANDEIA_POR_DEFECTO = "https://handeia.com";
991
+ var RUTA_TURNO = "/api/agent/space";
992
+ function acotar(x, y) {
993
+ const M = 8;
994
+ const vv = typeof window !== "undefined" ? window.visualViewport : null;
995
+ const w = vv?.width ?? window.innerWidth;
996
+ const h = vv?.height ?? window.innerHeight;
997
+ return {
998
+ x: Math.min(Math.max(x, M), Math.max(M, w - CIRCLE_SIZE - M)),
999
+ y: Math.min(Math.max(y, M), Math.max(M, h - CIRCLE_SIZE - M))
1000
+ };
1001
+ }
1002
+ function HandeiaAgent(props) {
1003
+ const base = (props.handeiaUrl ?? HANDEIA_POR_DEFECTO).replace(/\/$/, "");
1004
+ const [fieldOpen, setFieldOpen] = (0, import_react3.useState)(false);
1005
+ const [pos, setPos] = (0, import_react3.useState)(null);
1006
+ const drag = (0, import_react3.useRef)(null);
1007
+ const [texto, setTexto] = (0, import_react3.useState)("");
1008
+ const [model, setModel] = (0, import_react3.useState)(MODELS[0]?.id ?? "");
1009
+ const [fase, setFase] = (0, import_react3.useState)("idle");
1010
+ const [enviado, setEnviado] = (0, import_react3.useState)("");
1011
+ const [respuesta, setRespuesta] = (0, import_react3.useState)("");
1012
+ const historial = (0, import_react3.useRef)([]);
1013
+ const [pendiente, setPendiente] = (0, import_react3.useState)(null);
1014
+ const onDown = (e) => {
1015
+ const r = e.currentTarget.getBoundingClientRect();
1016
+ drag.current = {
1017
+ startX: e.clientX,
1018
+ startY: e.clientY,
1019
+ origX: pos?.x ?? r.left,
1020
+ origY: pos?.y ?? r.top,
1021
+ moved: false
1022
+ };
1023
+ e.currentTarget.setPointerCapture(e.pointerId);
1024
+ };
1025
+ const onMove = (e) => {
1026
+ const d = drag.current;
1027
+ if (!d) return;
1028
+ const dx = e.clientX - d.startX;
1029
+ const dy = e.clientY - d.startY;
1030
+ if (!d.moved && Math.hypot(dx, dy) < 6) return;
1031
+ d.moved = true;
1032
+ const { x, y } = acotar(d.origX + dx, d.origY + dy);
1033
+ const vv = window.visualViewport;
1034
+ setPos({
1035
+ x,
1036
+ y,
1037
+ openLeft: x > (vv?.width ?? window.innerWidth) / 2,
1038
+ openAbove: y > (vv?.height ?? window.innerHeight) / 2
1039
+ });
1040
+ };
1041
+ const onUp = () => {
1042
+ const d = drag.current;
1043
+ drag.current = null;
1044
+ if (d && !d.moved) setFieldOpen((v) => !v);
1045
+ };
1046
+ (0, import_react3.useEffect)(() => {
1047
+ const reacomodar = () => setPos((p) => p ? { ...p, ...acotar(p.x, p.y) } : p);
1048
+ window.addEventListener("resize", reacomodar);
1049
+ window.visualViewport?.addEventListener("resize", reacomodar);
1050
+ return () => {
1051
+ window.removeEventListener("resize", reacomodar);
1052
+ window.visualViewport?.removeEventListener("resize", reacomodar);
1053
+ };
1054
+ }, []);
1055
+ const turno = (0, import_react3.useCallback)(async (mensaje, actionResult) => {
1056
+ let context;
1057
+ try {
1058
+ context = await props.getContext?.();
1059
+ } catch {
1060
+ context = void 0;
1061
+ }
1062
+ let data;
1063
+ try {
1064
+ const res = await fetch(`${base}${RUTA_TURNO}`, {
1065
+ method: "POST",
1066
+ // La identidad va en la cookie de sesión de Handeia. El espacio nunca
1067
+ // ve ni toca el token del usuario.
1068
+ credentials: "include",
1069
+ headers: { "Content-Type": "application/json; charset=utf-8" },
1070
+ body: JSON.stringify({
1071
+ protocol: AGENT_PROTOCOL_VERSION,
1072
+ capId: props.capabilityId,
1073
+ message: mensaje,
1074
+ context,
1075
+ actions: props.actions ?? [],
1076
+ history: historial.current.slice(-12),
1077
+ actionResult
1078
+ })
1079
+ });
1080
+ data = await res.json();
1081
+ if (!res.ok || data.ok === false) {
1082
+ setRespuesta(
1083
+ data.error === "no_autenticado" ? "Inicia sesi\xF3n en Handeia para usar el asistente." : data.error === "espacio_no_instalado" ? "Este espacio no est\xE1 instalado en tu Handeia." : data.error === "cupo_agotado" ? "Se agot\xF3 el uso del asistente por hoy en este espacio." : "Handeia no pudo responder ahora mismo."
1084
+ );
1085
+ setFase("done");
1086
+ return;
1087
+ }
1088
+ } catch {
1089
+ setRespuesta("No pude comunicarme con Handeia.");
1090
+ setFase("done");
1091
+ return;
1092
+ }
1093
+ if (data.text) {
1094
+ setRespuesta(data.text);
1095
+ historial.current.push({ role: "agent", text: data.text });
1096
+ }
1097
+ setFase("done");
1098
+ if (!data.action) return;
1099
+ if (!props.onAction) {
1100
+ setRespuesta("Esto requiere una acci\xF3n que este espacio todav\xEDa no sabe ejecutar.");
1101
+ return;
1102
+ }
1103
+ if (data.confirm) {
1104
+ setPendiente({ name: data.action.name, args: data.action.args ?? {} });
1105
+ return;
1106
+ }
1107
+ await ejecutar(data.action.name, data.action.args ?? {}, mensaje);
1108
+ }, [base, props]);
1109
+ const ejecutar = (0, import_react3.useCallback)(async (name, args, mensaje) => {
1110
+ let r;
1111
+ try {
1112
+ r = await props.onAction(name, args);
1113
+ } catch (e) {
1114
+ r = { action: name, ok: false, error: e instanceof Error ? e.message : "fall\xF3" };
1115
+ }
1116
+ setFase("thinking");
1117
+ await turno(mensaje, r);
1118
+ }, [props, turno]);
1119
+ const enviar = (0, import_react3.useCallback)(() => {
1120
+ const t = texto.trim();
1121
+ if (!t || fase === "thinking") return;
1122
+ setTexto("");
1123
+ setEnviado(t);
1124
+ setRespuesta("");
1125
+ setPendiente(null);
1126
+ setFase("thinking");
1127
+ historial.current.push({ role: "user", text: t });
1128
+ void turno(t);
1129
+ }, [texto, fase, turno]);
1130
+ const sinMover = !pos;
1131
+ const left = sinMover ? "50%" : pos.openLeft ? pos.x - FIELD_GAP : pos.x + CIRCLE_SIZE + FIELD_GAP;
1132
+ const top = sinMover ? window?.innerHeight - 20 : pos.openAbove ? pos.y - FIELD_GAP : pos.y + CIRCLE_SIZE + FIELD_GAP;
1133
+ const tx = sinMover ? "-50%" : pos.openLeft ? "-100%" : "0%";
1134
+ const ty = sinMover ? "-100%" : pos.openAbove ? "-100%" : "0%";
1135
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1136
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.AnimatePresence, { children: fieldOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1137
+ import_react4.motion.div,
1138
+ {
1139
+ initial: { opacity: 0, scale: 0.97, y: 6 },
1140
+ animate: { opacity: 1, scale: 1, y: 0 },
1141
+ exit: { opacity: 0, scale: 0.98, y: 4 },
1142
+ transition: { duration: 0.2, ease: [0.16, 1, 0.3, 1] },
1143
+ style: { position: "fixed", left, top, transform: `translate(${tx}, ${ty})`, zIndex: 2147483e3 },
1144
+ className: "w-[min(560px,calc(100vw-32px))]",
1145
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative", children: [
1146
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1147
+ "button",
1148
+ {
1149
+ onClick: () => {
1150
+ setFieldOpen(false);
1151
+ setEnviado("");
1152
+ setRespuesta("");
1153
+ setFase("idle");
1154
+ },
1155
+ "aria-label": "Cerrar",
1156
+ className: "absolute -top-2.5 -right-2.5 z-10 w-6 h-6 rounded-full flex items-center justify-center text-white dark:text-black bg-black dark:bg-white shadow-[0_4px_14px_-2px_rgba(0,0,0,0.4)] transition-transform hover:scale-110 active:scale-95",
1157
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.X, { className: "w-3 h-3", strokeWidth: 2.4 })
1158
+ }
1159
+ ),
1160
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react4.AnimatePresence, { children: fase === "done" && respuesta && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1161
+ import_react4.motion.div,
1162
+ {
1163
+ initial: { opacity: 0, y: 6 },
1164
+ animate: { opacity: 1, y: 0 },
1165
+ exit: { opacity: 0 },
1166
+ transition: { duration: 0.3, ease: [0.16, 1, 0.3, 1] },
1167
+ className: "mb-4 flex flex-col items-center gap-2 text-center px-2",
1168
+ children: [
1169
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[11px] uppercase tracking-[0.15em] text-black/30 dark:text-white/55 truncate max-w-full", children: enviado }),
1170
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[17px] text-black/85 dark:text-white/92 tracking-[-0.02em] leading-relaxed", children: respuesta }),
1171
+ pendiente && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-1 flex items-center gap-2", children: [
1172
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1173
+ "button",
1174
+ {
1175
+ onClick: () => {
1176
+ const p = pendiente;
1177
+ setPendiente(null);
1178
+ void ejecutar(p.name, p.args, enviado);
1179
+ },
1180
+ className: "h-8 px-4 rounded-full bg-black dark:bg-white text-white dark:text-black text-[12px] tracking-[-0.01em]",
1181
+ children: "Hacerlo"
1182
+ }
1183
+ ),
1184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1185
+ "button",
1186
+ {
1187
+ onClick: () => {
1188
+ setPendiente(null);
1189
+ setRespuesta("Cancelado.");
1190
+ },
1191
+ className: "h-8 px-4 rounded-full border border-black/[0.12] dark:border-white/[0.18] text-black/70 dark:text-white/85 text-[12px]",
1192
+ children: "Cancelar"
1193
+ }
1194
+ )
1195
+ ] })
1196
+ ]
1197
+ },
1198
+ enviado
1199
+ ) }),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1201
+ InputBar,
1202
+ {
1203
+ value: texto,
1204
+ onChange: setTexto,
1205
+ onSend: enviar,
1206
+ aiPhase: fase,
1207
+ sentText: enviado,
1208
+ placeholder: props.placeholder ?? "Preg\xFAntale a Handeia\u2026",
1209
+ model,
1210
+ onModelChange: setModel
1211
+ }
1212
+ )
1213
+ ] })
1214
+ }
1215
+ ) }),
1216
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1217
+ "button",
1218
+ {
1219
+ onPointerDown: onDown,
1220
+ onPointerMove: onMove,
1221
+ onPointerUp: onUp,
1222
+ "aria-label": "Asistente de Handeia",
1223
+ style: {
1224
+ position: "fixed",
1225
+ zIndex: 2147483e3,
1226
+ ...pos ? { left: pos.x, top: pos.y } : { bottom: 20, right: 20 }
1227
+ },
1228
+ className: "w-11 h-11 rounded-full flex items-center justify-center text-white bg-black dark:bg-white dark:text-black shadow-[0_10px_30px_-6px_rgba(0,0,0,0.4)] transition-transform hover:scale-105 active:scale-95 touch-none cursor-grab active:cursor-grabbing",
1229
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Sparkles, { className: "w-4 h-4", strokeWidth: 1.9 })
1230
+ }
1231
+ )
1232
+ ] });
1233
+ }
1234
+ // Annotate the CommonJS export names for ESM import in node:
1235
+ 0 && (module.exports = {
1236
+ HandeiaAgent,
1237
+ InputBar,
1238
+ MODELS,
1239
+ VoiceCanvas
1240
+ });