ghostykit 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/chunks/{chunk-KZOMMG2D.js → chunk-D6JXJWYM.js} +12 -4
- package/dist/chunks/{chunk-XWOGK2HN.js → chunk-O6FCE4CS.js} +8 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/reducer.d.ts +5 -0
- package/dist/react/InlineMarkdown.d.ts +5 -0
- package/dist/react/Piloto.d.ts +8 -0
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +202 -148
- package/dist/styles.css +36 -5
- package/dist/transport/index.js +1 -1
- package/dist/transport/postStream.d.ts +3 -3
- package/dist/transport/types.d.ts +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,7 @@ const ref = useRef<PilotoHandle>(null);
|
|
|
107
107
|
agentName="Admin assistant"
|
|
108
108
|
welcome="How can I help with your crons?"
|
|
109
109
|
suggestions={["Why did the monitor fail?"]}
|
|
110
|
+
draft={{ text: "Why did the monitor fail?", key: row.id }} // prefilled + focused, not sent
|
|
110
111
|
context={() => ({ screen: "crons", selected })} // sent with EVERY turn
|
|
111
112
|
toolVerbs={{ run_cron: { one: "Ran a cron", many: "Ran %n crons", running: "Running the cron…" } }}
|
|
112
113
|
upload={async (file) => ({ url: await myUpload(file) })}
|
|
@@ -52,8 +52,16 @@ function parse(raw) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
function defaultHistory(json) {
|
|
55
|
-
const
|
|
56
|
-
|
|
55
|
+
const j = json;
|
|
56
|
+
const turns = (j?.messages ?? []).filter((m) => typeof m.text === "string").map((m) => ({ role: m.role === "user" ? "user" : "assistant", text: m.text }));
|
|
57
|
+
const permissions = (j?.pending ?? []).filter((p) => typeof p === "object" && p !== null && typeof p.id === "string").map((p) => ({
|
|
58
|
+
id: p.id,
|
|
59
|
+
title: String(p.title ?? ""),
|
|
60
|
+
...typeof p.tool === "string" ? { tool: p.tool } : {},
|
|
61
|
+
options: Array.isArray(p.options) ? p.options : [],
|
|
62
|
+
...p.expiresAt != null ? { expiresAt: p.expiresAt } : {}
|
|
63
|
+
}));
|
|
64
|
+
return { turns, permissions };
|
|
57
65
|
}
|
|
58
66
|
function uuid() {
|
|
59
67
|
const c = globalThis.crypto;
|
|
@@ -146,9 +154,9 @@ function createPostStreamTransport(target, options = {}) {
|
|
|
146
154
|
};
|
|
147
155
|
},
|
|
148
156
|
async history() {
|
|
149
|
-
if (!historyUrl) return [];
|
|
157
|
+
if (!historyUrl) return { turns: [] };
|
|
150
158
|
const res = await doFetch(historyUrl, { credentials, headers: { accept: "application/json" } });
|
|
151
|
-
if (!res.ok) return [];
|
|
159
|
+
if (!res.ok) return { turns: [] };
|
|
152
160
|
return (opts.parseHistory ?? defaultHistory)(await res.json().catch(() => null));
|
|
153
161
|
},
|
|
154
162
|
async send(input) {
|
|
@@ -411,6 +411,13 @@ function resolvePermission(prev, id, optionId, resolved, detail, asked) {
|
|
|
411
411
|
s = { ...s, turns };
|
|
412
412
|
return s;
|
|
413
413
|
}
|
|
414
|
+
function applyHistory(prev, turns, permissions = []) {
|
|
415
|
+
let s = { ...prev, effects: [] };
|
|
416
|
+
if (turns.length && !prev.turns.length) s.turns = turns;
|
|
417
|
+
const fresh = permissions.filter((p) => !prev.permissions.some((q) => q.id === p.id));
|
|
418
|
+
if (fresh.length) s.permissions = [...prev.permissions, ...fresh];
|
|
419
|
+
return s.turns === prev.turns && !fresh.length ? prev : s;
|
|
420
|
+
}
|
|
414
421
|
function appendAgentMessages(prev, texts) {
|
|
415
422
|
if (!texts.length) return prev;
|
|
416
423
|
return { ...prev, effects: [], turns: [...prev.turns, ...texts.map((text) => ({ role: "assistant", text, arrived: true }))] };
|
|
@@ -643,6 +650,7 @@ export {
|
|
|
643
650
|
stopTurn,
|
|
644
651
|
dropLastExchange,
|
|
645
652
|
resolvePermission,
|
|
653
|
+
applyHistory,
|
|
646
654
|
appendAgentMessages,
|
|
647
655
|
reconcileWith,
|
|
648
656
|
lastUserTurn,
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
-
export { RESTART_NOTICE, RESUME_TEXT, appendAgentMessages, beginSend, createPilotState, dropLastExchange, expiryMs, lastUserTurn, reconcileWith, reducePilotEvent, resolvePermission, resumeMode, sendAccepted, sendFailed, sendRejectedBusy, stopTurn, } from "./reducer";
|
|
2
|
+
export { RESTART_NOTICE, RESUME_TEXT, appendAgentMessages, applyHistory, beginSend, createPilotState, dropLastExchange, expiryMs, lastUserTurn, reconcileWith, reducePilotEvent, resolvePermission, resumeMode, sendAccepted, sendFailed, sendRejectedBusy, stopTurn, } from "./reducer";
|
|
3
3
|
export { reconcileTurns } from "./reconcile";
|
|
4
4
|
export { isNarration, narrationToMarkdown, splitNarration } from "./narration";
|
|
5
5
|
export { cleanReplyOption, extractReplyOptions, type ReplyOptions } from "./options";
|
package/dist/core/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
TOOL_VERBS_EN,
|
|
5
5
|
TOOL_VERBS_ES,
|
|
6
6
|
appendAgentMessages,
|
|
7
|
+
applyHistory,
|
|
7
8
|
beginSend,
|
|
8
9
|
cleanReplyOption,
|
|
9
10
|
createPilotState,
|
|
@@ -28,7 +29,7 @@ import {
|
|
|
28
29
|
stopTurn,
|
|
29
30
|
summarizeTools,
|
|
30
31
|
toolName
|
|
31
|
-
} from "../chunks/chunk-
|
|
32
|
+
} from "../chunks/chunk-O6FCE4CS.js";
|
|
32
33
|
import {
|
|
33
34
|
applyReferences,
|
|
34
35
|
cleanUserText,
|
|
@@ -41,6 +42,7 @@ export {
|
|
|
41
42
|
TOOL_VERBS_EN,
|
|
42
43
|
TOOL_VERBS_ES,
|
|
43
44
|
appendAgentMessages,
|
|
45
|
+
applyHistory,
|
|
44
46
|
applyReferences,
|
|
45
47
|
beginSend,
|
|
46
48
|
cleanReplyOption,
|
package/dist/core/reducer.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ export declare function dropLastExchange(prev: PilotState): PilotState;
|
|
|
36
36
|
export declare function resolvePermission(prev: PilotState, id: string, optionId: string, resolved: boolean, detail?: string,
|
|
37
37
|
/** La tarjeta como estaba al contestarla (el stream pudo retirarla mientras tanto). */
|
|
38
38
|
asked?: PermissionRequest): PilotState;
|
|
39
|
+
/**
|
|
40
|
+
* El historial guardado (al montar): el hilo, si la pantalla sigue vacía, y las tarjetas de permiso
|
|
41
|
+
* que siguen abiertas (sin duplicar las que el stream ya trajo).
|
|
42
|
+
*/
|
|
43
|
+
export declare function applyHistory(prev: PilotState, turns: Turn[], permissions?: PermissionRequest[]): PilotState;
|
|
39
44
|
/** Un mensaje del agente que entró fuera de un turno (el remate de los subagentes). */
|
|
40
45
|
export declare function appendAgentMessages(prev: PilotState, texts: string[]): PilotState;
|
|
41
46
|
/** La copia del hilo cambió: trae los mensajes que el usuario mandó desde OTRO cliente. Sólo en reposo. */
|
package/dist/react/Piloto.d.ts
CHANGED
|
@@ -65,6 +65,14 @@ export interface PilotoProps extends UsePilotOptions {
|
|
|
65
65
|
* `transport.respondPermission`. Devuelve `{resolved:false}` si ya no estaba abierto.
|
|
66
66
|
*/
|
|
67
67
|
onPermission?: PermissionHandler;
|
|
68
|
+
/**
|
|
69
|
+
* Una pregunta lista en la caja (sin mandarla) y el foco en ella: al abrir el copiloto desde una
|
|
70
|
+
* fila («Pídeselo»). Cambiar el texto —o `key`, para repetir el mismo— la vuelve a poner.
|
|
71
|
+
*/
|
|
72
|
+
draft?: string | {
|
|
73
|
+
text: string;
|
|
74
|
+
key?: string | number;
|
|
75
|
+
} | null;
|
|
68
76
|
/** Referencias controladas (si no, Piloto las guarda; agrégalas con `ref.addReference`). */
|
|
69
77
|
references?: Reference[];
|
|
70
78
|
onReferencesChange?: (refs: Reference[]) => void;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { usePilot, useSseTransport, type PermissionAnswer, type PermissionHandle
|
|
|
3
3
|
export { AgentBubble, CopyUserMessage, DeliveryChips, ReplyOptionButtons, TurnFooter, UsageStats, UserBubble, type AgentBubbleProps } from "./Bubble";
|
|
4
4
|
export { AgentSteps, ClawIcon, DEFAULT_TOOL_ICONS, Narration, ToolRow } from "./Steps";
|
|
5
5
|
export { LinkSafetyModal, Markdown } from "./Markdown";
|
|
6
|
+
export { InlineMarkdown, renderInline } from "./InlineMarkdown";
|
|
6
7
|
export { useTextAtPace } from "./TextAtPace";
|
|
7
8
|
export { Composer, ModelPicker, mimeOf, type ComposerProps, type UploadResult } from "./Composer";
|
|
8
9
|
export { ConfirmCard, DecisionLines } from "./ConfirmCard";
|
|
@@ -11,4 +12,4 @@ export { ConnectingState, TypingDots, WaitLabel } from "./Status";
|
|
|
11
12
|
export { PilotoContext, usePiloto, type MarkdownOptions, type PilotIcon, type PilotoConfig, type PilotTheme } from "./context";
|
|
12
13
|
export { STRINGS_EN, STRINGS_ES, type PilotStrings } from "./strings";
|
|
13
14
|
export * from "../core";
|
|
14
|
-
export { PilotHttpError, createPostStreamTransport, createSseTransport, readSse, type PilotListener, type PilotTransport, type PostStreamTransportOptions, type SseTransportOptions, type SubscribeOptions, } from "../transport";
|
|
15
|
+
export { PilotHttpError, createPostStreamTransport, createSseTransport, readSse, type PilotHistory, type PilotListener, type PilotTransport, type PostStreamTransportOptions, type SseTransportOptions, type SubscribeOptions, } from "../transport";
|
package/dist/react/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
TOOL_VERBS_EN,
|
|
5
5
|
TOOL_VERBS_ES,
|
|
6
6
|
appendAgentMessages,
|
|
7
|
+
applyHistory,
|
|
7
8
|
beginSend,
|
|
8
9
|
cleanReplyOption,
|
|
9
10
|
createPilotState,
|
|
@@ -28,13 +29,13 @@ import {
|
|
|
28
29
|
stopTurn,
|
|
29
30
|
summarizeTools,
|
|
30
31
|
toolName
|
|
31
|
-
} from "../chunks/chunk-
|
|
32
|
+
} from "../chunks/chunk-O6FCE4CS.js";
|
|
32
33
|
import {
|
|
33
34
|
PilotHttpError,
|
|
34
35
|
createPostStreamTransport,
|
|
35
36
|
createSseTransport,
|
|
36
37
|
readSse
|
|
37
|
-
} from "../chunks/chunk-
|
|
38
|
+
} from "../chunks/chunk-D6JXJWYM.js";
|
|
38
39
|
import {
|
|
39
40
|
applyReferences,
|
|
40
41
|
cleanUserText,
|
|
@@ -1312,14 +1313,46 @@ function Markdown({ children, animate = false, className }) {
|
|
|
1312
1313
|
|
|
1313
1314
|
// src/react/Steps.tsx
|
|
1314
1315
|
import { useState as useState4 } from "react";
|
|
1315
|
-
|
|
1316
|
+
|
|
1317
|
+
// src/react/InlineMarkdown.tsx
|
|
1318
|
+
import { Fragment as Fragment2 } from "react";
|
|
1319
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1320
|
+
var TOKEN = /(`[^`\n]+`)|(\*\*[^*\n]+\*\*|__[^_\n]+__)|(\*[^*\s][^*\n]*\*|_[^_\s][^_\n]*_)|(\[[^\]\n]+\]\(https?:\/\/[^)\s]+\))/g;
|
|
1321
|
+
function renderInline(text) {
|
|
1322
|
+
const out = [];
|
|
1323
|
+
let last = 0;
|
|
1324
|
+
let k = 0;
|
|
1325
|
+
for (const m of text.matchAll(TOKEN)) {
|
|
1326
|
+
const at = m.index ?? 0;
|
|
1327
|
+
if (at > last) out.push(text.slice(last, at));
|
|
1328
|
+
const t = m[0];
|
|
1329
|
+
if (m[1]) out.push(/* @__PURE__ */ jsx4("code", { children: t.slice(1, -1) }, k++));
|
|
1330
|
+
else if (m[2]) out.push(/* @__PURE__ */ jsx4("strong", { children: renderInline(t.slice(2, -2)) }, k++));
|
|
1331
|
+
else if (m[3]) out.push(/* @__PURE__ */ jsx4("em", { children: renderInline(t.slice(1, -1)) }, k++));
|
|
1332
|
+
else {
|
|
1333
|
+
const [, label, href] = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(t) ?? [];
|
|
1334
|
+
out.push(
|
|
1335
|
+
/* @__PURE__ */ jsx4("a", { href, target: "_blank", rel: "noopener noreferrer", children: label }, k++)
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
last = at + t.length;
|
|
1339
|
+
}
|
|
1340
|
+
if (last < text.length) out.push(text.slice(last));
|
|
1341
|
+
return out;
|
|
1342
|
+
}
|
|
1343
|
+
function InlineMarkdown({ children }) {
|
|
1344
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children: renderInline(children) });
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// src/react/Steps.tsx
|
|
1348
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1316
1349
|
function ClawIcon({ className }) {
|
|
1317
|
-
return /* @__PURE__ */
|
|
1318
|
-
/* @__PURE__ */
|
|
1319
|
-
/* @__PURE__ */
|
|
1320
|
-
/* @__PURE__ */
|
|
1321
|
-
/* @__PURE__ */
|
|
1322
|
-
/* @__PURE__ */
|
|
1350
|
+
return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round", className: `piloto-claw ${className ?? ""}`, "aria-hidden": "true", children: /* @__PURE__ */ jsxs3("g", { className: "piloto-claw-rig", children: [
|
|
1351
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 2v4" }),
|
|
1352
|
+
/* @__PURE__ */ jsx5("rect", { x: "8.5", y: "6", width: "7", height: "3.5", rx: "1" }),
|
|
1353
|
+
/* @__PURE__ */ jsx5("g", { className: "piloto-claw-l", children: /* @__PURE__ */ jsx5("path", { d: "M9.5 9.5 6 14l2 4.5" }) }),
|
|
1354
|
+
/* @__PURE__ */ jsx5("g", { className: "piloto-claw-r", children: /* @__PURE__ */ jsx5("path", { d: "M14.5 9.5 18 14l-2 4.5" }) }),
|
|
1355
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 9.5v4" })
|
|
1323
1356
|
] }) });
|
|
1324
1357
|
}
|
|
1325
1358
|
var DEFAULT_TOOL_ICONS = {
|
|
@@ -1369,8 +1402,8 @@ function AgentSteps({ tools, live }) {
|
|
|
1369
1402
|
const shown = groups.slice(-VISIBLE);
|
|
1370
1403
|
const hidden = groups.length - shown.length;
|
|
1371
1404
|
return /* @__PURE__ */ jsxs3("div", { className: "piloto-steps mb-3 rounded-(--piloto-radius-card) px-3 py-2.5 transition-[background-color,border-color] duration-300", "data-live": live || void 0, children: [
|
|
1372
|
-
/* @__PURE__ */
|
|
1373
|
-
/* @__PURE__ */
|
|
1405
|
+
/* @__PURE__ */ jsx5("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, title: open ? t.stepsCollapse : t.stepsExpand, className: "block w-full cursor-pointer text-left", children: open ? /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5 text-xs font-medium text-text-primary", children: [
|
|
1406
|
+
/* @__PURE__ */ jsx5(ChevronRight, { className: "h-3.5 w-3.5 shrink-0 rotate-90 text-text-tertiary" }),
|
|
1374
1407
|
/* @__PURE__ */ jsxs3("span", { className: "truncate", children: [
|
|
1375
1408
|
summarizeTools(tools, toolVerbs),
|
|
1376
1409
|
failed > 0 && /* @__PURE__ */ jsxs3("span", { className: "text-text-danger", children: [
|
|
@@ -1379,10 +1412,10 @@ function AgentSteps({ tools, live }) {
|
|
|
1379
1412
|
] })
|
|
1380
1413
|
] })
|
|
1381
1414
|
] }) : /* @__PURE__ */ jsxs3("ul", { className: "flex flex-col gap-1.5", children: [
|
|
1382
|
-
hidden > 0 && /* @__PURE__ */
|
|
1383
|
-
shown.map((g) => /* @__PURE__ */
|
|
1415
|
+
hidden > 0 && /* @__PURE__ */ jsx5("li", { className: "text-[11px] font-medium text-text-tertiary", children: t.earlierSteps(hidden) }),
|
|
1416
|
+
shown.map((g) => /* @__PURE__ */ jsx5(StepLine, { tool: g.tool, times: g.times, live }, g.tool.id))
|
|
1384
1417
|
] }) }),
|
|
1385
|
-
open && /* @__PURE__ */
|
|
1418
|
+
open && /* @__PURE__ */ jsx5("ul", { className: "mt-2 flex flex-col gap-1", children: tools.map((x) => /* @__PURE__ */ jsx5(ToolRow, { tool: x }, x.id)) })
|
|
1386
1419
|
] });
|
|
1387
1420
|
}
|
|
1388
1421
|
function StepLine({ tool, times, live }) {
|
|
@@ -1391,11 +1424,11 @@ function StepLine({ tool, times, live }) {
|
|
|
1391
1424
|
const spins = isRunning(tool) && live;
|
|
1392
1425
|
const label = stepLabel(tool, toolVerbs, t.step);
|
|
1393
1426
|
return /* @__PURE__ */ jsxs3("li", { className: "piloto-step flex min-w-0 items-center gap-2 text-[13px] leading-6 text-text-secondary", children: [
|
|
1394
|
-
/* @__PURE__ */
|
|
1395
|
-
/* @__PURE__ */
|
|
1427
|
+
/* @__PURE__ */ jsx5("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center", "aria-label": spins ? t.running : tool.status === "failed" ? t.failed : t.done, children: spins ? /* @__PURE__ */ jsx5(LoaderCircle, { className: "h-4 w-4 animate-spin text-text-info" }) : tool.status === "failed" ? /* @__PURE__ */ jsx5(CircleX, { className: "h-4 w-4 text-text-danger" }) : /* @__PURE__ */ jsx5(CircleCheck, { className: "h-4 w-4 text-text-success" }) }),
|
|
1428
|
+
/* @__PURE__ */ jsx5(Icon2, { className: "h-[22px] w-[22px] shrink-0 text-text-secondary" }),
|
|
1396
1429
|
/* @__PURE__ */ jsxs3("span", { className: "min-w-0 truncate", children: [
|
|
1397
|
-
/* @__PURE__ */
|
|
1398
|
-
tool.detail && /* @__PURE__ */
|
|
1430
|
+
/* @__PURE__ */ jsx5("span", { className: spins ? "text-text-primary" : void 0, children: label }),
|
|
1431
|
+
tool.detail && /* @__PURE__ */ jsx5("span", { className: "piloto-step-detail", children: tool.detail }),
|
|
1399
1432
|
times > 1 && /* @__PURE__ */ jsxs3("span", { className: "ml-1 tabular-nums text-text-tertiary", children: [
|
|
1400
1433
|
"\xD7",
|
|
1401
1434
|
times
|
|
@@ -1414,30 +1447,30 @@ function ToolRow({ tool }) {
|
|
|
1414
1447
|
const s = STATUS[tool.status ?? "pending"] ?? STATUS.pending;
|
|
1415
1448
|
const KindIcon = iconFor(tool, toolIcons);
|
|
1416
1449
|
return /* @__PURE__ */ jsxs3("li", { className: "piloto-tool-row flex items-start gap-2.5 rounded-(--piloto-radius-input) border border-border-secondary bg-background-secondary/50 px-3 py-2 transition-colors hover:border-border-primary hover:bg-background-secondary", children: [
|
|
1417
|
-
/* @__PURE__ */
|
|
1450
|
+
/* @__PURE__ */ jsx5("span", { className: "mt-px flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-background-primary text-text-secondary shadow-sm", children: /* @__PURE__ */ jsx5(KindIcon, { className: "h-3.5 w-3.5" }) }),
|
|
1418
1451
|
/* @__PURE__ */ jsxs3("span", { className: "min-w-0 flex-1", children: [
|
|
1419
|
-
/* @__PURE__ */
|
|
1420
|
-
tool.detail && /* @__PURE__ */
|
|
1421
|
-
tool.path && /* @__PURE__ */
|
|
1452
|
+
/* @__PURE__ */ jsx5("span", { className: "block min-w-0 truncate text-xs font-medium text-text-primary", children: stepLabel(tool, toolVerbs, tool.id) }),
|
|
1453
|
+
tool.detail && /* @__PURE__ */ jsx5("span", { className: "mt-0.5 block truncate text-[11px] text-text-tertiary", children: tool.detail }),
|
|
1454
|
+
tool.path && /* @__PURE__ */ jsx5("span", { className: "mt-0.5 block truncate font-mono text-[10px] text-text-tertiary", children: tool.path })
|
|
1422
1455
|
] }),
|
|
1423
|
-
/* @__PURE__ */
|
|
1456
|
+
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", title: t[s.key], "aria-label": t[s.key], children: /* @__PURE__ */ jsx5(s.Icon, { className: `h-3.5 w-3.5 ${s.className}` }) })
|
|
1424
1457
|
] });
|
|
1425
1458
|
}
|
|
1426
1459
|
function Narration({ steps, children }) {
|
|
1427
1460
|
return /* @__PURE__ */ jsxs3("ul", { className: "piloto-narration mb-3 flex flex-col gap-1.5", children: [
|
|
1428
1461
|
steps.map((p, i) => /* @__PURE__ */ jsxs3("li", { className: "piloto-fade-up flex items-start gap-2 text-text-secondary", children: [
|
|
1429
|
-
/* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1462
|
+
/* @__PURE__ */ jsx5("span", { className: "mt-[3px] grid size-4 shrink-0 place-items-center rounded-full bg-brand-500/15 text-brand-600", children: /* @__PURE__ */ jsx5(CheckMini, {}) }),
|
|
1463
|
+
/* @__PURE__ */ jsx5("span", { className: "piloto-narration-text min-w-0", children: /* @__PURE__ */ jsx5(InlineMarkdown, { children: p }) })
|
|
1431
1464
|
] }, i)),
|
|
1432
1465
|
children
|
|
1433
1466
|
] });
|
|
1434
1467
|
}
|
|
1435
1468
|
function CheckMini() {
|
|
1436
|
-
return /* @__PURE__ */
|
|
1469
|
+
return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 24 24", width: "11", height: "11", fill: "none", stroke: "currentColor", strokeWidth: 3, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("path", { d: "M20 6 9 17l-5-5" }) });
|
|
1437
1470
|
}
|
|
1438
1471
|
|
|
1439
1472
|
// src/react/Bubble.tsx
|
|
1440
|
-
import { Fragment as
|
|
1473
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1441
1474
|
async function copyText(text) {
|
|
1442
1475
|
try {
|
|
1443
1476
|
await navigator.clipboard.writeText(text);
|
|
@@ -1450,7 +1483,7 @@ function UserBubble({ turn }) {
|
|
|
1450
1483
|
const { strings: t, formatUserText } = usePiloto();
|
|
1451
1484
|
const note = systemNoteOf(turn.text);
|
|
1452
1485
|
if (note) {
|
|
1453
|
-
return /* @__PURE__ */
|
|
1486
|
+
return /* @__PURE__ */ jsx6("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxs4("span", { className: "piloto-system-note max-w-[80%] truncate rounded-full bg-background-secondary px-3 py-1 text-[11px] text-text-tertiary", children: [
|
|
1454
1487
|
"\u23F0 ",
|
|
1455
1488
|
note
|
|
1456
1489
|
] }) });
|
|
@@ -1464,23 +1497,23 @@ function UserBubble({ turn }) {
|
|
|
1464
1497
|
"\u21AA ",
|
|
1465
1498
|
t.steered
|
|
1466
1499
|
] }),
|
|
1467
|
-
docs.length > 0 && /* @__PURE__ */
|
|
1468
|
-
/* @__PURE__ */
|
|
1469
|
-
a.uri ? /* @__PURE__ */
|
|
1500
|
+
docs.length > 0 && /* @__PURE__ */ jsx6("div", { className: "mb-1 flex max-w-[75%] flex-wrap justify-end gap-2", children: docs.map((a, i) => /* @__PURE__ */ jsxs4("span", { className: "piloto-attachment-card inline-flex max-w-[16rem] items-center gap-1.5 rounded-(--piloto-radius-input) border border-border-primary bg-background-primary px-2.5 py-1.5 text-xs text-text-secondary", children: [
|
|
1501
|
+
/* @__PURE__ */ jsx6(FileText, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1502
|
+
a.uri ? /* @__PURE__ */ jsx6("a", { href: a.uri, target: "_blank", rel: "noopener noreferrer", className: "truncate hover:underline", children: a.name ?? a.mimeType }) : /* @__PURE__ */ jsx6("span", { className: "truncate", children: a.name ?? a.mimeType })
|
|
1470
1503
|
] }, i)) }),
|
|
1471
1504
|
(inBubble.length > 0 || visible.trim()) && /* @__PURE__ */ jsxs4("div", { className: "piloto-bubble-user max-w-[75%] whitespace-pre-wrap break-words rounded-(--piloto-radius-bubble) px-4 py-3 text-[15px] leading-[22px]", children: [
|
|
1472
|
-
inBubble.length > 0 && /* @__PURE__ */
|
|
1473
|
-
(a, i) => a.mimeType.startsWith("image/") ? /* @__PURE__ */
|
|
1505
|
+
inBubble.length > 0 && /* @__PURE__ */ jsx6("div", { className: `flex flex-wrap justify-end gap-1.5 ${visible.trim() ? "mb-2" : ""}`, children: inBubble.map(
|
|
1506
|
+
(a, i) => a.mimeType.startsWith("image/") ? /* @__PURE__ */ jsx6("img", { src: a.data ? `data:${a.mimeType};base64,${a.data}` : a.uri, alt: "", className: "h-20 w-20 rounded-lg object-cover" }, i) : /* @__PURE__ */ jsx6("audio", { src: a.uri, controls: true, preload: "metadata", className: "h-9 max-w-[16rem]" }, i)
|
|
1474
1507
|
) }),
|
|
1475
1508
|
visible
|
|
1476
1509
|
] }),
|
|
1477
|
-
visible.trim() && /* @__PURE__ */
|
|
1510
|
+
visible.trim() && /* @__PURE__ */ jsx6(CopyUserMessage, { text: visible })
|
|
1478
1511
|
] });
|
|
1479
1512
|
}
|
|
1480
1513
|
function CopyUserMessage({ text }) {
|
|
1481
1514
|
const { strings: t } = usePiloto();
|
|
1482
1515
|
const [copied, setCopied] = useState5(false);
|
|
1483
|
-
return /* @__PURE__ */
|
|
1516
|
+
return /* @__PURE__ */ jsx6(
|
|
1484
1517
|
"button",
|
|
1485
1518
|
{
|
|
1486
1519
|
type: "button",
|
|
@@ -1493,20 +1526,20 @@ function CopyUserMessage({ text }) {
|
|
|
1493
1526
|
"aria-label": copied ? t.copied : t.copyMessage,
|
|
1494
1527
|
title: copied ? t.copied : t.copy,
|
|
1495
1528
|
className: `piloto-copy-user grid h-7 w-7 place-items-center rounded-md text-text-tertiary transition-opacity hover:bg-background-secondary hover:text-text-primary focus-visible:opacity-100 [@media(hover:none)]:opacity-100 ${copied ? "opacity-100" : "opacity-0 group-hover/msg:opacity-100"}`,
|
|
1496
|
-
children: copied ? /* @__PURE__ */
|
|
1529
|
+
children: copied ? /* @__PURE__ */ jsx6(Check, { className: "h-3.5 w-3.5 text-text-success" }) : /* @__PURE__ */ jsx6(Copy, { className: "h-3.5 w-3.5" })
|
|
1497
1530
|
}
|
|
1498
1531
|
);
|
|
1499
1532
|
}
|
|
1500
1533
|
function AnswerText({ text, live, whole = false }) {
|
|
1501
1534
|
const { visible, animate } = useTextAtPace(text, live, whole);
|
|
1502
1535
|
if (!visible) return null;
|
|
1503
|
-
return /* @__PURE__ */
|
|
1536
|
+
return /* @__PURE__ */ jsx6(Markdown, { animate, children: visible });
|
|
1504
1537
|
}
|
|
1505
1538
|
function ReplyOptionButtons({ options, onPick }) {
|
|
1506
1539
|
const { strings: t } = usePiloto();
|
|
1507
|
-
return /* @__PURE__ */
|
|
1540
|
+
return /* @__PURE__ */ jsx6("ul", { className: "piloto-options mt-3 flex flex-col gap-2", "aria-label": t.replyOptions, children: options.items.map((item, i) => {
|
|
1508
1541
|
const clean = cleanReplyOption(item);
|
|
1509
|
-
return /* @__PURE__ */
|
|
1542
|
+
return /* @__PURE__ */ jsx6("li", { className: "piloto-chunk-in", children: /* @__PURE__ */ jsxs4(
|
|
1510
1543
|
"button",
|
|
1511
1544
|
{
|
|
1512
1545
|
type: "button",
|
|
@@ -1517,8 +1550,8 @@ function ReplyOptionButtons({ options, onPick }) {
|
|
|
1517
1550
|
i + 1,
|
|
1518
1551
|
"."
|
|
1519
1552
|
] }),
|
|
1520
|
-
/* @__PURE__ */
|
|
1521
|
-
/* @__PURE__ */
|
|
1553
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0 flex-1", children: clean }),
|
|
1554
|
+
/* @__PURE__ */ jsx6(ArrowUpRight, { className: "h-4 w-4 shrink-0 text-text-tertiary transition-colors group-hover:text-text-primary" })
|
|
1522
1555
|
]
|
|
1523
1556
|
}
|
|
1524
1557
|
) }, i);
|
|
@@ -1530,12 +1563,12 @@ function UsageStats({ used, size, cost }) {
|
|
|
1530
1563
|
const pct = size > 0 ? Math.min(100, Math.round(used / size * 100)) : 0;
|
|
1531
1564
|
return /* @__PURE__ */ jsxs4("div", { className: "piloto-usage mt-2 flex items-center gap-4 text-[11px] text-text-tertiary", children: [
|
|
1532
1565
|
/* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1", children: [
|
|
1533
|
-
/* @__PURE__ */
|
|
1566
|
+
/* @__PURE__ */ jsx6(Gauge, { className: "h-3 w-3" }),
|
|
1534
1567
|
used.toLocaleString(t.numberLocale),
|
|
1535
1568
|
size > 0 && ` / ${size.toLocaleString(t.numberLocale)} (${pct}%)`
|
|
1536
1569
|
] }),
|
|
1537
1570
|
cost > 0 && /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1", children: [
|
|
1538
|
-
/* @__PURE__ */
|
|
1571
|
+
/* @__PURE__ */ jsx6(Coins, { className: "h-3 w-3" }),
|
|
1539
1572
|
cost.toLocaleString(t.numberLocale, { style: "currency", currency: "USD", maximumFractionDigits: 4 })
|
|
1540
1573
|
] })
|
|
1541
1574
|
] });
|
|
@@ -1544,7 +1577,7 @@ function TurnFooter({ text, seconds, onRetry, showCopy = true }) {
|
|
|
1544
1577
|
const { strings: t } = usePiloto();
|
|
1545
1578
|
const [copied, setCopied] = useState5(false);
|
|
1546
1579
|
return /* @__PURE__ */ jsxs4("div", { className: "piloto-footer mt-2 flex items-center gap-3 text-xs text-text-tertiary", children: [
|
|
1547
|
-
seconds ? /* @__PURE__ */
|
|
1580
|
+
seconds ? /* @__PURE__ */ jsx6("span", { children: t.worked(seconds) }) : null,
|
|
1548
1581
|
showCopy && /* @__PURE__ */ jsxs4(
|
|
1549
1582
|
"button",
|
|
1550
1583
|
{
|
|
@@ -1557,13 +1590,13 @@ function TurnFooter({ text, seconds, onRetry, showCopy = true }) {
|
|
|
1557
1590
|
},
|
|
1558
1591
|
className: "flex items-center gap-1 transition-colors hover:text-text-primary",
|
|
1559
1592
|
children: [
|
|
1560
|
-
copied ? /* @__PURE__ */
|
|
1593
|
+
copied ? /* @__PURE__ */ jsx6(Check, { className: "h-3.5 w-3.5 text-text-success" }) : /* @__PURE__ */ jsx6(Copy, { className: "h-3.5 w-3.5" }),
|
|
1561
1594
|
copied ? t.copied : t.copy
|
|
1562
1595
|
]
|
|
1563
1596
|
}
|
|
1564
1597
|
),
|
|
1565
1598
|
onRetry && /* @__PURE__ */ jsxs4("button", { type: "button", onClick: onRetry, className: "flex items-center gap-1 transition-colors hover:text-text-primary", children: [
|
|
1566
|
-
/* @__PURE__ */
|
|
1599
|
+
/* @__PURE__ */ jsx6(RotateCcw, { className: "h-3.5 w-3.5" }),
|
|
1567
1600
|
t.retry
|
|
1568
1601
|
] })
|
|
1569
1602
|
] });
|
|
@@ -1571,9 +1604,9 @@ function TurnFooter({ text, seconds, onRetry, showCopy = true }) {
|
|
|
1571
1604
|
function DeliveryChips({ deliveries }) {
|
|
1572
1605
|
const shown = deliveries.filter((d) => !d.capture);
|
|
1573
1606
|
if (!shown.length) return null;
|
|
1574
|
-
return /* @__PURE__ */
|
|
1575
|
-
/* @__PURE__ */
|
|
1576
|
-
/* @__PURE__ */
|
|
1607
|
+
return /* @__PURE__ */ jsx6("div", { className: "mt-2 flex flex-wrap gap-1.5", children: shown.map((d, i) => /* @__PURE__ */ jsxs4("span", { className: "piloto-delivery inline-flex max-w-[16rem] items-center gap-1.5 rounded-(--piloto-radius-input) border border-border-primary px-2 py-1 text-xs text-text-secondary", children: [
|
|
1608
|
+
/* @__PURE__ */ jsx6(FileText, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
1609
|
+
/* @__PURE__ */ jsx6("span", { className: "truncate", children: d.name })
|
|
1577
1610
|
] }, i)) });
|
|
1578
1611
|
}
|
|
1579
1612
|
function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy = true, renderDeliveries }) {
|
|
@@ -1583,28 +1616,28 @@ function AgentBubble({ turn, live, onRetry, onOption, showUsage = true, showCopy
|
|
|
1583
1616
|
const options = onOption && !live && visible ? extractReplyOptions(visible) : null;
|
|
1584
1617
|
return /* @__PURE__ */ jsxs4("div", { className: "piloto-agent min-w-0 max-w-[92%]", children: [
|
|
1585
1618
|
turn.thought && /* @__PURE__ */ jsxs4("details", { className: "piloto-thought mb-3 text-xs text-text-secondary", children: [
|
|
1586
|
-
/* @__PURE__ */
|
|
1587
|
-
/* @__PURE__ */
|
|
1619
|
+
/* @__PURE__ */ jsx6("summary", { className: "cursor-pointer select-none", children: t.thinking }),
|
|
1620
|
+
/* @__PURE__ */ jsx6("p", { className: "mt-2 whitespace-pre-wrap border-l-2 border-border-secondary pl-3", children: turn.thought })
|
|
1588
1621
|
] }),
|
|
1589
|
-
turn.tools && turn.tools.length > 0 && /* @__PURE__ */
|
|
1590
|
-
narrated.steps.length > 0 && /* @__PURE__ */
|
|
1591
|
-
options ? /* @__PURE__ */ jsxs4(
|
|
1592
|
-
options.before && /* @__PURE__ */
|
|
1593
|
-
/* @__PURE__ */
|
|
1622
|
+
turn.tools && turn.tools.length > 0 && /* @__PURE__ */ jsx6(AgentSteps, { tools: turn.tools, live }),
|
|
1623
|
+
narrated.steps.length > 0 && /* @__PURE__ */ jsx6(Narration, { steps: narrated.steps }),
|
|
1624
|
+
options ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1625
|
+
options.before && /* @__PURE__ */ jsx6(AnswerText, { text: options.before, live }),
|
|
1626
|
+
/* @__PURE__ */ jsx6(ReplyOptionButtons, { options, onPick: onOption })
|
|
1594
1627
|
] }) : (
|
|
1595
1628
|
// Cada tramo nuevo tras una herramienta monta su propio ritmo (empieza de cero). Lo que llegó
|
|
1596
1629
|
// entero fuera de un turno se escribe al mismo ritmo; al recargar ya sale completo.
|
|
1597
|
-
visible && /* @__PURE__ */
|
|
1630
|
+
visible && /* @__PURE__ */ jsx6(AnswerText, { text: visible, live: live || !!turn.arrived, whole: !!turn.arrived }, `r${narrated.steps.length}`)
|
|
1598
1631
|
),
|
|
1599
|
-
renderDeliveries ? renderDeliveries(turn, live) : turn.deliveries?.length ? /* @__PURE__ */
|
|
1600
|
-
turn.decisions?.length ? /* @__PURE__ */
|
|
1601
|
-
showUsage && turn.usage && /* @__PURE__ */
|
|
1602
|
-
turn.text && !live && /* @__PURE__ */
|
|
1632
|
+
renderDeliveries ? renderDeliveries(turn, live) : turn.deliveries?.length ? /* @__PURE__ */ jsx6(DeliveryChips, { deliveries: turn.deliveries }) : null,
|
|
1633
|
+
turn.decisions?.length ? /* @__PURE__ */ jsx6(DecisionLines, { decisions: turn.decisions }) : null,
|
|
1634
|
+
showUsage && turn.usage && /* @__PURE__ */ jsx6(UsageStats, { ...turn.usage }),
|
|
1635
|
+
turn.text && !live && /* @__PURE__ */ jsx6(TurnFooter, { text: turn.text, seconds: turn.seconds, onRetry, showCopy })
|
|
1603
1636
|
] });
|
|
1604
1637
|
}
|
|
1605
1638
|
|
|
1606
1639
|
// src/react/Chips.tsx
|
|
1607
|
-
import { jsx as
|
|
1640
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1608
1641
|
function ReferenceChips({
|
|
1609
1642
|
references,
|
|
1610
1643
|
onRemove,
|
|
@@ -1612,29 +1645,29 @@ function ReferenceChips({
|
|
|
1612
1645
|
}) {
|
|
1613
1646
|
const { strings: t } = usePiloto();
|
|
1614
1647
|
if (!references.length) return null;
|
|
1615
|
-
return /* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
1648
|
+
return /* @__PURE__ */ jsx7("div", { className: "piloto-references flex flex-wrap gap-1.5", children: references.map((r) => /* @__PURE__ */ jsxs5("span", { className: "piloto-reference inline-flex max-w-full items-center gap-1.5 rounded-full border py-1 pl-2.5 pr-1 text-xs text-text-primary", children: [
|
|
1649
|
+
/* @__PURE__ */ jsx7(AtSign, { className: "h-3 w-3 shrink-0 text-brand-600" }),
|
|
1650
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onActivate?.(r), className: "shrink-0 font-semibold underline-offset-2 hover:underline", children: r.label }),
|
|
1618
1651
|
r.hint && /* @__PURE__ */ jsxs5("span", { className: "truncate text-text-secondary", children: [
|
|
1619
1652
|
"\xAB",
|
|
1620
1653
|
r.hint,
|
|
1621
1654
|
"\xBB"
|
|
1622
1655
|
] }),
|
|
1623
|
-
/* @__PURE__ */
|
|
1656
|
+
/* @__PURE__ */ jsx7(
|
|
1624
1657
|
"button",
|
|
1625
1658
|
{
|
|
1626
1659
|
type: "button",
|
|
1627
1660
|
onClick: () => onRemove(r.id),
|
|
1628
1661
|
"aria-label": t.removeReference,
|
|
1629
1662
|
className: "grid size-5 shrink-0 cursor-pointer place-items-center rounded-full text-text-tertiary hover:bg-background-secondary hover:text-text-primary",
|
|
1630
|
-
children: /* @__PURE__ */
|
|
1663
|
+
children: /* @__PURE__ */ jsx7(X, { className: "h-3 w-3" })
|
|
1631
1664
|
}
|
|
1632
1665
|
)
|
|
1633
1666
|
] }, r.id)) });
|
|
1634
1667
|
}
|
|
1635
1668
|
function Suggestions({ items, onPick, onPrefill }) {
|
|
1636
1669
|
if (!items.length) return null;
|
|
1637
|
-
return /* @__PURE__ */
|
|
1670
|
+
return /* @__PURE__ */ jsx7("div", { className: "piloto-suggestions mt-3 flex flex-wrap justify-center gap-2", children: items.map((s, i) => /* @__PURE__ */ jsx7(
|
|
1638
1671
|
"button",
|
|
1639
1672
|
{
|
|
1640
1673
|
type: "button",
|
|
@@ -1648,7 +1681,7 @@ function Suggestions({ items, onPick, onPrefill }) {
|
|
|
1648
1681
|
|
|
1649
1682
|
// src/react/Composer.tsx
|
|
1650
1683
|
import { useCallback, useEffect as useEffect3, useRef as useRef3, useState as useState6 } from "react";
|
|
1651
|
-
import { Fragment as
|
|
1684
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1652
1685
|
var MAX_HEIGHT = 240;
|
|
1653
1686
|
var BY_EXTENSION = {
|
|
1654
1687
|
png: "image/png",
|
|
@@ -1940,13 +1973,13 @@ function Composer({
|
|
|
1940
1973
|
};
|
|
1941
1974
|
const canSend = Boolean(value.trim() || files.length > 0 || references.length > 0);
|
|
1942
1975
|
const iconButton = "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-background-secondary hover:text-text-primary disabled:opacity-30";
|
|
1943
|
-
return /* @__PURE__ */
|
|
1944
|
-
withFiles && dragging && /* @__PURE__ */
|
|
1945
|
-
/* @__PURE__ */
|
|
1946
|
-
/* @__PURE__ */
|
|
1947
|
-
/* @__PURE__ */
|
|
1976
|
+
return /* @__PURE__ */ jsx8("div", { className: "piloto-composer rounded-(--piloto-radius-input)", children: /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-2 p-3", children: [
|
|
1977
|
+
withFiles && dragging && /* @__PURE__ */ jsx8("div", { className: "piloto-drop pointer-events-none fixed inset-0 z-[1000] flex items-center justify-center p-6", children: /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center gap-2 rounded-(--piloto-radius-card) border-2 border-dashed border-brand-500 bg-background-primary px-10 py-8 text-center shadow-(--piloto-shadow-card)", children: [
|
|
1978
|
+
/* @__PURE__ */ jsx8(Paperclip, { className: "size-7 text-brand-600" }),
|
|
1979
|
+
/* @__PURE__ */ jsx8("p", { className: "text-base font-semibold text-text-primary", children: t.dropFiles }),
|
|
1980
|
+
/* @__PURE__ */ jsx8("p", { className: "text-xs text-text-secondary", children: t.dropHint })
|
|
1948
1981
|
] }) }),
|
|
1949
|
-
/* @__PURE__ */
|
|
1982
|
+
/* @__PURE__ */ jsx8(
|
|
1950
1983
|
"input",
|
|
1951
1984
|
{
|
|
1952
1985
|
ref: fileRef,
|
|
@@ -1959,36 +1992,36 @@ function Composer({
|
|
|
1959
1992
|
}
|
|
1960
1993
|
}
|
|
1961
1994
|
),
|
|
1962
|
-
notice && /* @__PURE__ */
|
|
1995
|
+
notice && /* @__PURE__ */ jsx8("p", { className: "text-xs text-text-danger", children: notice }),
|
|
1963
1996
|
uploading && /* @__PURE__ */ jsxs6("p", { className: "flex items-center gap-1.5 text-xs text-text-secondary", children: [
|
|
1964
|
-
/* @__PURE__ */
|
|
1997
|
+
/* @__PURE__ */ jsx8(LoaderCircle, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
1965
1998
|
files.length > 1 ? t.uploadingN(files.length) : t.uploading
|
|
1966
1999
|
] }),
|
|
1967
|
-
files.length > 0 && /* @__PURE__ */
|
|
1968
|
-
mimeOf(a.file).startsWith("image/") ? /* @__PURE__ */
|
|
2000
|
+
files.length > 0 && /* @__PURE__ */ jsx8("div", { className: "flex flex-wrap gap-2", children: files.map((a) => /* @__PURE__ */ jsxs6("div", { className: "relative h-16 w-16 overflow-hidden rounded-lg border border-border-primary bg-background-secondary", children: [
|
|
2001
|
+
mimeOf(a.file).startsWith("image/") ? /* @__PURE__ */ jsx8("img", { src: a.url, alt: a.file.name, className: "h-full w-full object-cover" }) : (
|
|
1969
2002
|
// Un PDF pintado con <img> es un recuadro roto: nombre, extensión y peso.
|
|
1970
2003
|
/* @__PURE__ */ jsxs6("span", { className: "flex h-full w-full flex-col items-center justify-center gap-1 px-1 text-center", title: a.file.name, children: [
|
|
1971
|
-
/* @__PURE__ */
|
|
1972
|
-
/* @__PURE__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
2004
|
+
/* @__PURE__ */ jsx8(FileText, { className: "h-4 w-4 shrink-0 text-text-tertiary" }),
|
|
2005
|
+
/* @__PURE__ */ jsx8("span", { className: "w-full truncate text-[9px] font-medium uppercase leading-none text-text-secondary", children: a.file.name.match(/\.([A-Za-z0-9]+)$/)?.[1] ?? "file" }),
|
|
2006
|
+
/* @__PURE__ */ jsx8("span", { className: "w-full truncate text-[9px] leading-none text-text-tertiary", children: shortSize(a.file.size) })
|
|
1974
2007
|
] })
|
|
1975
2008
|
),
|
|
1976
|
-
/* @__PURE__ */
|
|
2009
|
+
/* @__PURE__ */ jsx8(
|
|
1977
2010
|
"button",
|
|
1978
2011
|
{
|
|
1979
2012
|
type: "button",
|
|
1980
2013
|
onClick: () => removeFile(a.id),
|
|
1981
2014
|
"aria-label": t.removeAttachment,
|
|
1982
2015
|
className: "absolute right-0.5 top-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-background-inverse/80 text-text-inverse shadow-sm transition-colors hover:bg-background-inverse",
|
|
1983
|
-
children: /* @__PURE__ */
|
|
2016
|
+
children: /* @__PURE__ */ jsx8(X, { className: "h-3 w-3" })
|
|
1984
2017
|
}
|
|
1985
2018
|
)
|
|
1986
2019
|
] }, a.id)) }),
|
|
1987
|
-
/* @__PURE__ */
|
|
2020
|
+
/* @__PURE__ */ jsx8(ReferenceChips, { references, onRemove: (id) => onRemoveReference?.(id), onActivate: onActivateReference }),
|
|
1988
2021
|
recording ? (
|
|
1989
2022
|
// Grabando: la barra sustituye al campo de texto — punto rojo, reloj, la onda y las dos salidas.
|
|
1990
2023
|
/* @__PURE__ */ jsxs6("div", { className: "flex min-h-[24px] items-center gap-3 px-1", children: [
|
|
1991
|
-
/* @__PURE__ */
|
|
2024
|
+
/* @__PURE__ */ jsx8(
|
|
1992
2025
|
"button",
|
|
1993
2026
|
{
|
|
1994
2027
|
type: "button",
|
|
@@ -1998,19 +2031,19 @@ function Composer({
|
|
|
1998
2031
|
},
|
|
1999
2032
|
"aria-label": t.cancelVoice,
|
|
2000
2033
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-text-secondary hover:bg-background-secondary hover:text-text-primary",
|
|
2001
|
-
children: /* @__PURE__ */
|
|
2034
|
+
children: /* @__PURE__ */ jsx8(X, { className: "h-4 w-4" })
|
|
2002
2035
|
}
|
|
2003
2036
|
),
|
|
2004
|
-
/* @__PURE__ */
|
|
2037
|
+
/* @__PURE__ */ jsx8("span", { className: "piloto-rec-dot h-2 w-2 shrink-0 rounded-full" }),
|
|
2005
2038
|
/* @__PURE__ */ jsxs6("span", { className: "w-10 shrink-0 font-mono text-xs tabular-nums text-text-secondary", children: [
|
|
2006
2039
|
Math.floor(seconds / 60),
|
|
2007
2040
|
":",
|
|
2008
2041
|
String(seconds % 60).padStart(2, "0")
|
|
2009
2042
|
] }),
|
|
2010
|
-
/* @__PURE__ */
|
|
2011
|
-
/* @__PURE__ */
|
|
2043
|
+
/* @__PURE__ */ jsx8("div", { className: "flex h-6 min-w-0 flex-1 items-center gap-[2px] overflow-hidden", children: Array.from({ length: 60 }, (_, i) => wave[wave.length - 60 + i] ?? 0).map((v, i) => /* @__PURE__ */ jsx8("span", { className: "w-[3px] shrink-0 rounded-full bg-text-secondary/70", style: { height: `${Math.max(3, Math.round(v * 22))}px` } }, i)) }),
|
|
2044
|
+
/* @__PURE__ */ jsx8("button", { type: "button", onClick: toggleRecording, "aria-label": t.stopAndSendVoice, className: "piloto-send flex h-8 w-8 shrink-0 items-center justify-center rounded-full", children: /* @__PURE__ */ jsx8(ArrowUp, { className: "h-4 w-4" }) })
|
|
2012
2045
|
] })
|
|
2013
|
-
) : /* @__PURE__ */
|
|
2046
|
+
) : /* @__PURE__ */ jsx8(
|
|
2014
2047
|
"textarea",
|
|
2015
2048
|
{
|
|
2016
2049
|
ref,
|
|
@@ -2032,8 +2065,8 @@ function Composer({
|
|
|
2032
2065
|
),
|
|
2033
2066
|
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2034
2067
|
/* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 flex-1 items-center gap-1.5 overflow-hidden", children: [
|
|
2035
|
-
withFiles && /* @__PURE__ */ jsxs6(
|
|
2036
|
-
/* @__PURE__ */
|
|
2068
|
+
withFiles && /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
2069
|
+
/* @__PURE__ */ jsx8(
|
|
2037
2070
|
"button",
|
|
2038
2071
|
{
|
|
2039
2072
|
type: "button",
|
|
@@ -2046,10 +2079,10 @@ function Composer({
|
|
|
2046
2079
|
"aria-label": canSeeImages ? t.attachImage : t.noImages,
|
|
2047
2080
|
title: canSeeImages ? t.attachImage : t.noImages,
|
|
2048
2081
|
className: iconButton,
|
|
2049
|
-
children: /* @__PURE__ */
|
|
2082
|
+
children: /* @__PURE__ */ jsx8(Image, { className: "h-4 w-4" })
|
|
2050
2083
|
}
|
|
2051
2084
|
),
|
|
2052
|
-
/* @__PURE__ */
|
|
2085
|
+
/* @__PURE__ */ jsx8(
|
|
2053
2086
|
"button",
|
|
2054
2087
|
{
|
|
2055
2088
|
type: "button",
|
|
@@ -2062,16 +2095,16 @@ function Composer({
|
|
|
2062
2095
|
"aria-label": t.attachFile,
|
|
2063
2096
|
title: t.attachFile,
|
|
2064
2097
|
className: iconButton,
|
|
2065
|
-
children: /* @__PURE__ */
|
|
2098
|
+
children: /* @__PURE__ */ jsx8(Paperclip, { className: "h-4 w-4" })
|
|
2066
2099
|
}
|
|
2067
2100
|
)
|
|
2068
2101
|
] }),
|
|
2069
2102
|
extras,
|
|
2070
|
-
models.length > 0 && onModelChange && /* @__PURE__ */
|
|
2103
|
+
models.length > 0 && onModelChange && /* @__PURE__ */ jsx8(ModelPicker, { models, current: currentModel, onChange: onModelChange })
|
|
2071
2104
|
] }),
|
|
2072
2105
|
/* @__PURE__ */ jsxs6("div", { className: "flex shrink-0 items-center gap-1.5", children: [
|
|
2073
|
-
canRecord && !recording && /* @__PURE__ */
|
|
2074
|
-
/* @__PURE__ */
|
|
2106
|
+
canRecord && !recording && /* @__PURE__ */ jsx8("button", { type: "button", onClick: toggleRecording, disabled: busy || transcribing, "aria-label": t.recordVoice, title: t.voiceNote, className: `${iconButton} h-8 w-8`, children: transcribing ? /* @__PURE__ */ jsx8(LoaderCircle, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx8(Mic, { className: "h-4 w-4" }) }),
|
|
2107
|
+
/* @__PURE__ */ jsx8(
|
|
2075
2108
|
"button",
|
|
2076
2109
|
{
|
|
2077
2110
|
type: "button",
|
|
@@ -2080,7 +2113,7 @@ function Composer({
|
|
|
2080
2113
|
"aria-label": busy ? t.stop : uploading ? t.uploading : t.send,
|
|
2081
2114
|
"data-ready": busy || canSend || uploading || void 0,
|
|
2082
2115
|
className: "piloto-send flex h-8 w-8 items-center justify-center rounded-full transition-colors",
|
|
2083
|
-
children: busy ? /* @__PURE__ */
|
|
2116
|
+
children: busy ? /* @__PURE__ */ jsx8(Square, { className: "h-3 w-3 fill-current" }) : uploading ? /* @__PURE__ */ jsx8(LoaderCircle, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx8(ArrowUp, { className: "h-4 w-4" })
|
|
2084
2117
|
}
|
|
2085
2118
|
)
|
|
2086
2119
|
] })
|
|
@@ -2115,13 +2148,13 @@ function ModelPicker({ models, current, onChange }) {
|
|
|
2115
2148
|
onClick: () => setOpen((v) => !v),
|
|
2116
2149
|
className: "flex max-w-56 items-center gap-1.5 rounded-full border border-border-primary px-2.5 py-1 text-xs text-text-secondary transition-colors hover:bg-background-secondary hover:text-text-primary",
|
|
2117
2150
|
children: [
|
|
2118
|
-
/* @__PURE__ */
|
|
2119
|
-
/* @__PURE__ */
|
|
2120
|
-
/* @__PURE__ */
|
|
2151
|
+
/* @__PURE__ */ jsx8(Cpu, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
2152
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate", children: active?.name ?? t.model }),
|
|
2153
|
+
/* @__PURE__ */ jsx8(ChevronDown, { className: "h-3.5 w-3.5 shrink-0 opacity-70" })
|
|
2121
2154
|
]
|
|
2122
2155
|
}
|
|
2123
2156
|
),
|
|
2124
|
-
open && /* @__PURE__ */
|
|
2157
|
+
open && /* @__PURE__ */ jsx8("div", { className: "piloto-menu absolute bottom-full left-0 z-20 mb-1 max-h-80 min-w-52 overflow-y-auto rounded-(--piloto-radius-input) border border-border-primary bg-background-primary py-1 shadow-(--piloto-shadow-card)", children: models.map((m) => /* @__PURE__ */ jsxs6(
|
|
2125
2158
|
"button",
|
|
2126
2159
|
{
|
|
2127
2160
|
type: "button",
|
|
@@ -2131,8 +2164,8 @@ function ModelPicker({ models, current, onChange }) {
|
|
|
2131
2164
|
},
|
|
2132
2165
|
className: "flex w-full items-center justify-between gap-3 px-3 py-1.5 text-left text-xs text-text-secondary transition-colors hover:bg-background-secondary hover:text-text-primary",
|
|
2133
2166
|
children: [
|
|
2134
|
-
/* @__PURE__ */
|
|
2135
|
-
m.value === current && /* @__PURE__ */
|
|
2167
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate", children: m.name }),
|
|
2168
|
+
m.value === current && /* @__PURE__ */ jsx8(Check, { className: "h-3.5 w-3.5 shrink-0 text-text-success" })
|
|
2136
2169
|
]
|
|
2137
2170
|
},
|
|
2138
2171
|
m.value
|
|
@@ -2143,11 +2176,11 @@ function ModelPicker({ models, current, onChange }) {
|
|
|
2143
2176
|
// src/react/Status.tsx
|
|
2144
2177
|
import { AnimatePresence, motion } from "motion/react";
|
|
2145
2178
|
import { useEffect as useEffect4, useState as useState7 } from "react";
|
|
2146
|
-
import { jsx as
|
|
2179
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2147
2180
|
var WAKING_GIVEUP_S = 45;
|
|
2148
2181
|
var STILL_WORKING_S = 90;
|
|
2149
2182
|
function TypingDots() {
|
|
2150
|
-
return /* @__PURE__ */
|
|
2183
|
+
return /* @__PURE__ */ jsx9("div", { className: "piloto-typing", "aria-hidden": true, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx9("span", { className: "piloto-typing-dot" }, i)) });
|
|
2151
2184
|
}
|
|
2152
2185
|
function WaitLabel({
|
|
2153
2186
|
phase,
|
|
@@ -2170,15 +2203,15 @@ function WaitLabel({
|
|
|
2170
2203
|
elsewhere || seconds >= STILL_WORKING_S ? t.stillWorking : phrases[Math.floor(seconds / 4) % phrases.length]
|
|
2171
2204
|
) : elsewhere ? t.stillWorking : t.howItGoes(seconds);
|
|
2172
2205
|
const lost = phase === "waking" && seconds >= WAKING_GIVEUP_S;
|
|
2173
|
-
return /* @__PURE__ */
|
|
2206
|
+
return /* @__PURE__ */ jsx9("div", { className: "piloto-wait flex flex-col gap-0.5 text-sm text-text-secondary", children: lost ? /* @__PURE__ */ jsxs7("p", { className: "text-text-primary", children: [
|
|
2174
2207
|
t.cantStart,
|
|
2175
2208
|
" ",
|
|
2176
|
-
/* @__PURE__ */
|
|
2209
|
+
/* @__PURE__ */ jsx9("button", { type: "button", onClick: onLost, className: "cursor-pointer underline underline-offset-2", children: t.retry })
|
|
2177
2210
|
] }) : (
|
|
2178
2211
|
// La frase cambia con fundido (sale y entra): de golpe se leía brusco.
|
|
2179
2212
|
/* @__PURE__ */ jsxs7("p", { className: "flex items-center", children: [
|
|
2180
|
-
/* @__PURE__ */
|
|
2181
|
-
showDuration && /* @__PURE__ */
|
|
2213
|
+
/* @__PURE__ */ jsx9(AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ jsx9(motion.span, { className: "piloto-wait-text", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.3, ease: "easeInOut" }, children: what }, what) }),
|
|
2214
|
+
showDuration && /* @__PURE__ */ jsx9("span", { className: "piloto-wait-time", children: formatDuration(seconds) })
|
|
2182
2215
|
] })
|
|
2183
2216
|
) });
|
|
2184
2217
|
}
|
|
@@ -2188,7 +2221,7 @@ function ConnectingState({ phase, error, avatar, onRetry }) {
|
|
|
2188
2221
|
const current = PHASES.indexOf(phase);
|
|
2189
2222
|
return /* @__PURE__ */ jsxs7(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.3 }, className: "piloto-connecting flex min-h-[40vh] flex-col items-center justify-center gap-8 text-center", children: [
|
|
2190
2223
|
/* @__PURE__ */ jsxs7("div", { className: "relative flex h-20 w-20 items-center justify-center", children: [
|
|
2191
|
-
!error && [0, 1].map((i) => /* @__PURE__ */
|
|
2224
|
+
!error && [0, 1].map((i) => /* @__PURE__ */ jsx9(
|
|
2192
2225
|
motion.span,
|
|
2193
2226
|
{
|
|
2194
2227
|
className: "absolute inset-0 rounded-full border border-border-primary",
|
|
@@ -2198,34 +2231,34 @@ function ConnectingState({ phase, error, avatar, onRetry }) {
|
|
|
2198
2231
|
},
|
|
2199
2232
|
i
|
|
2200
2233
|
)),
|
|
2201
|
-
/* @__PURE__ */
|
|
2234
|
+
/* @__PURE__ */ jsx9(
|
|
2202
2235
|
motion.div,
|
|
2203
2236
|
{
|
|
2204
2237
|
className: "grid h-12 w-12 place-items-center",
|
|
2205
2238
|
animate: error ? { scale: 1, filter: "grayscale(1)" } : { scale: [1, 1.06, 1] },
|
|
2206
2239
|
transition: error ? { duration: 0.3 } : { duration: 2.2, repeat: Infinity, ease: "easeInOut" },
|
|
2207
|
-
children: avatar ?? /* @__PURE__ */
|
|
2240
|
+
children: avatar ?? /* @__PURE__ */ jsx9("span", { className: "piloto-avatar-default" })
|
|
2208
2241
|
}
|
|
2209
2242
|
)
|
|
2210
2243
|
] }),
|
|
2211
|
-
/* @__PURE__ */
|
|
2244
|
+
/* @__PURE__ */ jsx9("ol", { className: "flex flex-col gap-3 text-left", children: t.connectSteps.map((step, i) => {
|
|
2212
2245
|
const done = !error && i < current;
|
|
2213
2246
|
const active = !error && i === current;
|
|
2214
2247
|
const failed = !!error && i === current;
|
|
2215
2248
|
return /* @__PURE__ */ jsxs7("li", { className: "flex items-center gap-3", children: [
|
|
2216
|
-
/* @__PURE__ */
|
|
2249
|
+
/* @__PURE__ */ jsx9("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center", children: done ? /* @__PURE__ */ jsx9("span", { className: "piloto-pop flex h-5 w-5 items-center justify-center rounded-full bg-background-success", children: /* @__PURE__ */ jsx9(Check, { className: "h-3 w-3 text-text-inverse", strokeWidth: 3 }) }) : active ? /* @__PURE__ */ jsx9("span", { className: "piloto-pulse h-2.5 w-2.5 rounded-full bg-text-primary" }) : failed ? /* @__PURE__ */ jsx9("span", { className: "h-2.5 w-2.5 rounded-full bg-text-danger" }) : /* @__PURE__ */ jsx9("span", { className: "h-2 w-2 rounded-full bg-border-primary" }) }),
|
|
2217
2250
|
/* @__PURE__ */ jsxs7("div", { className: "leading-tight", children: [
|
|
2218
2251
|
/* @__PURE__ */ jsxs7("p", { className: `text-sm ${active || failed ? "text-text-primary" : done ? "text-text-secondary" : "text-text-tertiary"}`, children: [
|
|
2219
2252
|
step.label,
|
|
2220
|
-
active && /* @__PURE__ */
|
|
2253
|
+
active && /* @__PURE__ */ jsx9("span", { className: "piloto-ellipsis inline-block w-4 text-left", children: "\u2026" })
|
|
2221
2254
|
] }),
|
|
2222
|
-
active && /* @__PURE__ */
|
|
2255
|
+
active && /* @__PURE__ */ jsx9("p", { className: "text-xs text-text-tertiary", children: step.hint })
|
|
2223
2256
|
] })
|
|
2224
2257
|
] }, i);
|
|
2225
2258
|
}) }),
|
|
2226
2259
|
error && /* @__PURE__ */ jsxs7("div", { className: "flex max-w-md flex-col items-center gap-3", children: [
|
|
2227
|
-
/* @__PURE__ */
|
|
2228
|
-
onRetry && /* @__PURE__ */
|
|
2260
|
+
/* @__PURE__ */ jsx9("p", { className: "text-sm text-text-danger", children: error }),
|
|
2261
|
+
onRetry && /* @__PURE__ */ jsx9("button", { type: "button", onClick: onRetry, className: "rounded-full border border-border-primary px-4 py-1.5 text-sm text-text-primary hover:bg-background-secondary", children: t.connectRetry })
|
|
2229
2262
|
] })
|
|
2230
2263
|
] });
|
|
2231
2264
|
}
|
|
@@ -2279,8 +2312,10 @@ function usePilot(transport, opts = {}) {
|
|
|
2279
2312
|
);
|
|
2280
2313
|
};
|
|
2281
2314
|
if (transport.history && !optsRef.current.initialTurns) {
|
|
2282
|
-
transport.history().then((
|
|
2283
|
-
if (
|
|
2315
|
+
transport.history().then((h) => {
|
|
2316
|
+
if (gone) return;
|
|
2317
|
+
const { turns, permissions } = Array.isArray(h) ? { turns: h, permissions: [] } : h;
|
|
2318
|
+
commit(applyHistory(ref.current, turns, permissions));
|
|
2284
2319
|
}).catch(() => {
|
|
2285
2320
|
}).finally(subscribe);
|
|
2286
2321
|
} else subscribe();
|
|
@@ -2401,7 +2436,7 @@ function useSseTransport(base, opts) {
|
|
|
2401
2436
|
}
|
|
2402
2437
|
|
|
2403
2438
|
// src/react/Piloto.tsx
|
|
2404
|
-
import { Fragment as
|
|
2439
|
+
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2405
2440
|
var useIsoLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect;
|
|
2406
2441
|
var ANCHOR_GAP = 24;
|
|
2407
2442
|
var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
@@ -2435,8 +2470,17 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2435
2470
|
},
|
|
2436
2471
|
[props.references, props.onReferencesChange]
|
|
2437
2472
|
);
|
|
2438
|
-
const [draft, setDraftState] = useState9({ text: "", n: 0 });
|
|
2439
2473
|
const composerBox = useRef5(null);
|
|
2474
|
+
const draftText = typeof props.draft === "string" ? props.draft : props.draft?.text;
|
|
2475
|
+
const draftKey = typeof props.draft === "object" && props.draft ? props.draft.key : void 0;
|
|
2476
|
+
const [draft, setDraftState] = useState9(() => ({ text: draftText ?? "", n: 0 }));
|
|
2477
|
+
const draftSeen = useRef5(`${draftText}\0${draftKey}`);
|
|
2478
|
+
useEffect6(() => {
|
|
2479
|
+
const id = `${draftText}\0${draftKey}`;
|
|
2480
|
+
if (draftText == null || id === draftSeen.current) return;
|
|
2481
|
+
draftSeen.current = id;
|
|
2482
|
+
setDraftState((d) => ({ text: draftText, n: d.n + 1 }));
|
|
2483
|
+
}, [draftText, draftKey]);
|
|
2440
2484
|
const send = useCallback3(
|
|
2441
2485
|
(text, attachments = []) => {
|
|
2442
2486
|
const current = refsRef.current;
|
|
@@ -2567,7 +2611,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2567
2611
|
const id = setTimeout(() => setSlowConnect(true), 800);
|
|
2568
2612
|
return () => clearTimeout(id);
|
|
2569
2613
|
}, [connected]);
|
|
2570
|
-
const composer = /* @__PURE__ */
|
|
2614
|
+
const composer = /* @__PURE__ */ jsx10("div", { ref: composerBox, children: /* @__PURE__ */ jsx10(
|
|
2571
2615
|
Composer,
|
|
2572
2616
|
{
|
|
2573
2617
|
initialValue: draft.text,
|
|
@@ -2591,20 +2635,27 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2591
2635
|
},
|
|
2592
2636
|
draft.n
|
|
2593
2637
|
) });
|
|
2594
|
-
const permissionCards = cards.map((p) => /* @__PURE__ */
|
|
2595
|
-
return /* @__PURE__ */
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2638
|
+
const permissionCards = cards.map((p) => /* @__PURE__ */ jsx10(ConfirmCard, { request: p, onRespond: respond, onSettled: settle, onExpire: pilot.expirePermission }, p.id));
|
|
2639
|
+
return /* @__PURE__ */ jsx10(PilotoContext.Provider, { value: config, children: /* @__PURE__ */ jsx10("div", { ...rootProps(theme, props.skin), "data-busy": busy || void 0, className: `piloto ${props.className ?? ""}`, style: props.style, children: empty ? (
|
|
2640
|
+
// Si no cabe (panel bajo, saludo largo), se desplaza en vez de recortarse: el centrado es con
|
|
2641
|
+
// márgenes automáticos, que nunca empujan contenido fuera del contenedor.
|
|
2642
|
+
/* @__PURE__ */ jsxs8("div", { className: "piloto-empty", children: [
|
|
2643
|
+
slots.headerActions && /* @__PURE__ */ jsx10("div", { className: "absolute right-4 top-3 flex gap-2", children: slots.headerActions }),
|
|
2644
|
+
/* @__PURE__ */ jsxs8("div", { className: "piloto-empty-inner", children: [
|
|
2645
|
+
!connected && slowConnect ? /* @__PURE__ */ jsx10(ConnectingState, { phase, error, avatar: props.avatar }) : slots.emptyState ?? /* @__PURE__ */ jsxs8("div", { className: "piloto-welcome mb-6 flex flex-col items-center gap-3 text-center", children: [
|
|
2646
|
+
props.avatar,
|
|
2647
|
+
props.welcome && // Un saludo largo (una explicación) va en letra de lectura; uno corto, en la de cartel.
|
|
2648
|
+
/* @__PURE__ */ jsx10("div", { className: "piloto-welcome-text", "data-long": typeof props.welcome === "string" && props.welcome.length > 60 ? "" : void 0, children: props.welcome })
|
|
2649
|
+
] }),
|
|
2650
|
+
/* @__PURE__ */ jsxs8("div", { className: "w-full max-w-3xl", children: [
|
|
2651
|
+
slots.aboveComposer,
|
|
2652
|
+
composer,
|
|
2653
|
+
props.suggestions && /* @__PURE__ */ jsx10(Suggestions, { items: props.suggestions, onPick: (s) => send(s), onPrefill: (s) => setDraftState((d) => ({ text: s, n: d.n + 1 })) })
|
|
2654
|
+
] })
|
|
2655
|
+
] })
|
|
2605
2656
|
] })
|
|
2606
|
-
|
|
2607
|
-
slots.headerActions && /* @__PURE__ */
|
|
2657
|
+
) : /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
2658
|
+
slots.headerActions && /* @__PURE__ */ jsx10("div", { className: "flex shrink-0 justify-end gap-2 px-4 pt-3", children: slots.headerActions }),
|
|
2608
2659
|
/* @__PURE__ */ jsxs8(
|
|
2609
2660
|
"div",
|
|
2610
2661
|
{
|
|
@@ -2625,7 +2676,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2625
2676
|
turns.map((turn, i) => {
|
|
2626
2677
|
const isLast = i === turns.length - 1;
|
|
2627
2678
|
const live = busy && isLast;
|
|
2628
|
-
return /* @__PURE__ */
|
|
2679
|
+
return /* @__PURE__ */ jsx10("div", { "data-turn": i, children: turn.role === "user" ? /* @__PURE__ */ jsx10(UserBubble, { turn }) : /* @__PURE__ */ jsx10(
|
|
2629
2680
|
AgentBubble,
|
|
2630
2681
|
{
|
|
2631
2682
|
turn,
|
|
@@ -2640,30 +2691,30 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2640
2691
|
}),
|
|
2641
2692
|
permissionCards,
|
|
2642
2693
|
busy && !toolRunning && !cards.length && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1.5", children: [
|
|
2643
|
-
/* @__PURE__ */
|
|
2644
|
-
/* @__PURE__ */
|
|
2694
|
+
/* @__PURE__ */ jsx10(TypingDots, {}),
|
|
2695
|
+
/* @__PURE__ */ jsx10(WaitLabel, { phase, since: state.turnStartedAt, elsewhere: state.elsewhere, onLost: pilot.abandonAndRetry, showDuration: features.duration !== false })
|
|
2645
2696
|
] }),
|
|
2646
2697
|
interrupted && !busy && /* @__PURE__ */ jsxs8("p", { className: "piloto-interrupted text-sm text-text-secondary", children: [
|
|
2647
2698
|
/me quedé a medias|i stopped halfway/i.test(last?.text ?? "") ? t.interruptedOurFault : t.interrupted(interrupted.reason),
|
|
2648
2699
|
" ",
|
|
2649
|
-
/* @__PURE__ */
|
|
2700
|
+
/* @__PURE__ */ jsx10("button", { type: "button", onClick: pilot.resume, className: "cursor-pointer text-text-primary underline underline-offset-2", children: interrupted.canContinue ? t.continueTurn : t.resume })
|
|
2650
2701
|
] }),
|
|
2651
|
-
notice && /* @__PURE__ */
|
|
2652
|
-
error && /* @__PURE__ */
|
|
2702
|
+
notice && /* @__PURE__ */ jsx10("p", { className: "piloto-notice text-sm text-text-tertiary", children: notice }),
|
|
2703
|
+
error && /* @__PURE__ */ jsx10("p", { className: "piloto-error text-sm text-text-danger", children: error })
|
|
2653
2704
|
] }),
|
|
2654
|
-
/* @__PURE__ */
|
|
2705
|
+
/* @__PURE__ */ jsx10("div", { ref: spacer, "aria-hidden": true })
|
|
2655
2706
|
]
|
|
2656
2707
|
}
|
|
2657
2708
|
),
|
|
2658
2709
|
/* @__PURE__ */ jsxs8("div", { className: "piloto-dock relative mx-auto w-full px-4 pb-4", children: [
|
|
2659
|
-
!atBottom && /* @__PURE__ */
|
|
2710
|
+
!atBottom && /* @__PURE__ */ jsx10(
|
|
2660
2711
|
"button",
|
|
2661
2712
|
{
|
|
2662
2713
|
type: "button",
|
|
2663
2714
|
onClick: toBottom,
|
|
2664
2715
|
"aria-label": t.goToLatest,
|
|
2665
2716
|
className: "piloto-to-bottom absolute -top-12 left-1/2 z-10 flex h-9 w-9 -translate-x-1/2 items-center justify-center rounded-full border border-border-primary bg-background-secondary text-text-secondary shadow-(--piloto-shadow-card) transition hover:text-text-primary",
|
|
2666
|
-
children: /* @__PURE__ */
|
|
2717
|
+
children: /* @__PURE__ */ jsx10(ArrowDown, { className: "h-4 w-4" })
|
|
2667
2718
|
}
|
|
2668
2719
|
),
|
|
2669
2720
|
slots.aboveComposer,
|
|
@@ -2682,6 +2733,7 @@ export {
|
|
|
2682
2733
|
DEFAULT_TOOL_ICONS,
|
|
2683
2734
|
DecisionLines,
|
|
2684
2735
|
DeliveryChips,
|
|
2736
|
+
InlineMarkdown,
|
|
2685
2737
|
LinkSafetyModal,
|
|
2686
2738
|
Markdown,
|
|
2687
2739
|
ModelPicker,
|
|
@@ -2705,6 +2757,7 @@ export {
|
|
|
2705
2757
|
UserBubble,
|
|
2706
2758
|
WaitLabel,
|
|
2707
2759
|
appendAgentMessages,
|
|
2760
|
+
applyHistory,
|
|
2708
2761
|
applyReferences,
|
|
2709
2762
|
beginSend,
|
|
2710
2763
|
cleanReplyOption,
|
|
@@ -2727,6 +2780,7 @@ export {
|
|
|
2727
2780
|
reconcileTurns,
|
|
2728
2781
|
reconcileWith,
|
|
2729
2782
|
reducePilotEvent,
|
|
2783
|
+
renderInline,
|
|
2730
2784
|
resolvePermission,
|
|
2731
2785
|
resumeMode,
|
|
2732
2786
|
sendAccepted,
|
package/dist/styles.css
CHANGED
|
@@ -844,9 +844,6 @@
|
|
|
844
844
|
.piloto .pb-4 {
|
|
845
845
|
padding-bottom: calc(var(--spacing) * 4);
|
|
846
846
|
}
|
|
847
|
-
.piloto .pb-\[8vh\] {
|
|
848
|
-
padding-bottom: 8vh;
|
|
849
|
-
}
|
|
850
847
|
.piloto .pl-2\.5 {
|
|
851
848
|
padding-left: calc(var(--spacing) * 2.5);
|
|
852
849
|
}
|
|
@@ -1710,6 +1707,7 @@
|
|
|
1710
1707
|
line-height: 1.55;
|
|
1711
1708
|
-webkit-font-smoothing: antialiased;
|
|
1712
1709
|
isolation: isolate;
|
|
1710
|
+
container-type: inline-size;
|
|
1713
1711
|
}
|
|
1714
1712
|
|
|
1715
1713
|
/* ── Ghosty, oscuro: la sombra dura se vuelve lavanda (negro sobre negro no se ve). ── */
|
|
@@ -2072,14 +2070,47 @@
|
|
|
2072
2070
|
border: var(--piloto-border-width) solid var(--piloto-border);
|
|
2073
2071
|
box-shadow: 3px 3px 0 var(--piloto-shadow-color);
|
|
2074
2072
|
}
|
|
2075
|
-
|
|
2073
|
+
/* El estado vacío: si no cabe, se desplaza (nunca recorta el saludo ni la sombra del composer). */
|
|
2074
|
+
.piloto .piloto-empty {
|
|
2075
|
+
position: relative;
|
|
2076
|
+
display: flex;
|
|
2077
|
+
flex-direction: column;
|
|
2078
|
+
flex: 1 1 auto;
|
|
2079
|
+
min-height: 0;
|
|
2080
|
+
overflow-y: auto;
|
|
2081
|
+
padding: 16px 16px 24px;
|
|
2082
|
+
}
|
|
2083
|
+
.piloto .piloto-empty-inner {
|
|
2084
|
+
margin: auto 0;
|
|
2085
|
+
width: 100%;
|
|
2086
|
+
display: flex;
|
|
2087
|
+
flex-direction: column;
|
|
2088
|
+
align-items: center;
|
|
2089
|
+
padding-bottom: 6vh;
|
|
2090
|
+
}
|
|
2091
|
+
.piloto .piloto-welcome-text {
|
|
2076
2092
|
font-family: var(--piloto-font-display);
|
|
2077
2093
|
font-weight: 800;
|
|
2078
|
-
|
|
2094
|
+
/* Al ancho del CONTENEDOR (un panel lateral no es la ventana). */
|
|
2095
|
+
font-size: clamp(26px, 8cqi, 44px);
|
|
2079
2096
|
line-height: 1;
|
|
2080
2097
|
letter-spacing: 0.01em;
|
|
2081
2098
|
max-width: 18ch;
|
|
2099
|
+
color: var(--piloto-text);
|
|
2100
|
+
text-wrap: balance;
|
|
2101
|
+
}
|
|
2102
|
+
.piloto .piloto-welcome-text[data-long] {
|
|
2103
|
+
font-family: var(--piloto-font);
|
|
2104
|
+
font-weight: 600;
|
|
2105
|
+
font-size: clamp(16px, 4.2cqi, 19px);
|
|
2106
|
+
line-height: 1.45;
|
|
2107
|
+
letter-spacing: 0;
|
|
2108
|
+
max-width: 44ch;
|
|
2109
|
+
color: var(--piloto-text-2);
|
|
2082
2110
|
}
|
|
2111
|
+
.piloto .piloto-narration-text code { background: var(--piloto-surface-2); padding: 0.05rem 0.3rem; border-radius: 4px; font-size: 0.88em; font-family: var(--piloto-font-mono); }
|
|
2112
|
+
.piloto .piloto-narration-text strong { font-weight: 700; color: var(--piloto-text); }
|
|
2113
|
+
.piloto .piloto-narration-text a { text-decoration: underline; text-decoration-color: var(--piloto-accent); text-underline-offset: 2px; }
|
|
2083
2114
|
.piloto .piloto-wait-time { margin-left: 0.7em; color: var(--piloto-text-3); font-variant-numeric: tabular-nums; }
|
|
2084
2115
|
.piloto .piloto-system-note { border: var(--piloto-hairline) solid var(--piloto-border-2); }
|
|
2085
2116
|
|
package/dist/transport/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SendInput, Turn } from "../core/types";
|
|
2
|
-
import { type PilotTransport } from "./types";
|
|
2
|
+
import { type PilotHistory, type PilotTransport } from "./types";
|
|
3
3
|
/** Un evento SSE ya separado: nombre + data crudo. */
|
|
4
4
|
export interface SseMessage {
|
|
5
5
|
event: string;
|
|
@@ -22,8 +22,8 @@ export interface PostStreamTransportOptions {
|
|
|
22
22
|
attachUrl?: string | null;
|
|
23
23
|
/** URL del historial (JSON). Default `url`; `null` = sin historial. */
|
|
24
24
|
historyUrl?: string | null;
|
|
25
|
-
/** Cómo leer el historial. Default: `{messages:[{role:"user"|"agent",text}]}`. */
|
|
26
|
-
parseHistory?: (json: unknown) => Turn[];
|
|
25
|
+
/** Cómo leer el historial. Default: `{messages:[{role:"user"|"agent",text}], pending:[permission…]}`. */
|
|
26
|
+
parseHistory?: (json: unknown) => Turn[] | PilotHistory;
|
|
27
27
|
/** URL para cancelar (POST). Sin ella, «detener» sólo deja de escuchar: el turno sigue en el servidor. */
|
|
28
28
|
cancelUrl?: string;
|
|
29
29
|
/** Silencio (sin un solo byte, ni `: ka`) tras el que la conexión se da por muerta. Default 60 s. */
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { SendInput, SendResult, Turn } from "../core/types";
|
|
1
|
+
import type { PermissionRequest, SendInput, SendResult, Turn } from "../core/types";
|
|
2
|
+
/** El hilo guardado y las tarjetas de permiso que siguen abiertas (EasyBits: `pending`). */
|
|
3
|
+
export interface PilotHistory {
|
|
4
|
+
turns: Turn[];
|
|
5
|
+
permissions?: PermissionRequest[];
|
|
6
|
+
}
|
|
2
7
|
/** Un evento con nombre y su `data` ya parseado (`null` si no traía o no era JSON). */
|
|
3
8
|
export type PilotListener = (name: string, data: unknown) => void;
|
|
4
9
|
export interface SubscribeOptions {
|
|
@@ -19,7 +24,7 @@ export interface PilotTransport {
|
|
|
19
24
|
resolved: boolean;
|
|
20
25
|
}>;
|
|
21
26
|
/** El hilo guardado, para pintarlo al montar (si la app no pasa `initialTurns`). */
|
|
22
|
-
history?(): Promise<Turn[]>;
|
|
27
|
+
history?(): Promise<Turn[] | PilotHistory>;
|
|
23
28
|
/**
|
|
24
29
|
* ¿Se puede mandar un mensaje con un turno en vuelo (steer)? Default `true` (Ghosty). Con
|
|
25
30
|
* `false` (el servidor contesta 409) el composer no deja mandar hasta que el turno cierre.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghostykit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Piloto UI \u2014 the in-app copilot chat from Ghosty Studio: streaming, agent steps, attachments, voice, reconnection and human approval cards. Pure event reducer + SSE transport + React components.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/core/index.js",
|