ghostykit 0.3.0 → 0.3.2
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.
|
@@ -141,6 +141,8 @@ function createPostStreamTransport(target, options = {}) {
|
|
|
141
141
|
return {
|
|
142
142
|
// Sin steer: con un turno corriendo el servidor contesta 409. El composer no deja mandar.
|
|
143
143
|
steer: false,
|
|
144
|
+
// Cada turno trae su stream: no hay «conectando» que enseñar.
|
|
145
|
+
stream: "per-turn",
|
|
144
146
|
subscribe(l, sub = {}) {
|
|
145
147
|
listener = l;
|
|
146
148
|
isBusy = sub.isBusy ?? (() => false);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Sin conexión por este rato, se enseña. */
|
|
2
|
+
export declare const CONNECTING_DELAY_MS = 1200;
|
|
3
|
+
/** Una vez a la vista, se queda al menos esto. */
|
|
4
|
+
export declare const CONNECTING_MIN_MS = 700;
|
|
5
|
+
export interface ConnectingGate {
|
|
6
|
+
/** Desde cuándo está a la vista (null = oculto). */
|
|
7
|
+
shownAt: number | null;
|
|
8
|
+
/** Desde cuándo falta la conexión (null = conectado). */
|
|
9
|
+
waitingSince: number | null;
|
|
10
|
+
}
|
|
11
|
+
export declare const initialGate: (now: number, connected: boolean) => ConnectingGate;
|
|
12
|
+
/** El estado a `now`, y cuándo volver a mirar (null = no hace falta). */
|
|
13
|
+
export declare function stepGate(g: ConnectingGate, connected: boolean, now: number, delay?: number, min?: number): {
|
|
14
|
+
gate: ConnectingGate;
|
|
15
|
+
visible: boolean;
|
|
16
|
+
recheckAt: number | null;
|
|
17
|
+
};
|
package/dist/react/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
createPostStreamTransport,
|
|
36
36
|
createSseTransport,
|
|
37
37
|
readSse
|
|
38
|
-
} from "../chunks/chunk-
|
|
38
|
+
} from "../chunks/chunk-XZHX2NZT.js";
|
|
39
39
|
import {
|
|
40
40
|
applyReferences,
|
|
41
41
|
cleanUserText,
|
|
@@ -43,6 +43,9 @@ import {
|
|
|
43
43
|
systemNoteOf
|
|
44
44
|
} from "../chunks/chunk-6U545WJG.js";
|
|
45
45
|
|
|
46
|
+
// src/react/Piloto.tsx
|
|
47
|
+
import { AnimatePresence as AnimatePresence3 } from "motion/react";
|
|
48
|
+
|
|
46
49
|
// ../../node_modules/lucide-react/dist/esm/createLucideIcon.mjs
|
|
47
50
|
import { forwardRef as forwardRef2, createElement as createElement3 } from "react";
|
|
48
51
|
|
|
@@ -1862,7 +1865,7 @@ function Composer({
|
|
|
1862
1865
|
const id = requestAnimationFrame(() => {
|
|
1863
1866
|
const el = ref.current;
|
|
1864
1867
|
if (!el) return;
|
|
1865
|
-
el.focus();
|
|
1868
|
+
el.focus({ preventScroll: true });
|
|
1866
1869
|
el.setSelectionRange(el.value.length, el.value.length);
|
|
1867
1870
|
});
|
|
1868
1871
|
return () => cancelAnimationFrame(id);
|
|
@@ -1915,7 +1918,7 @@ function Composer({
|
|
|
1915
1918
|
return next;
|
|
1916
1919
|
});
|
|
1917
1920
|
setNotice(rejected.length ? rejected.join(" \xB7 ") : null);
|
|
1918
|
-
ref.current?.focus();
|
|
1921
|
+
ref.current?.focus({ preventScroll: true });
|
|
1919
1922
|
},
|
|
1920
1923
|
[busy, canSeeImages, upload, maxFiles, maxFileMb, t]
|
|
1921
1924
|
);
|
|
@@ -2296,6 +2299,22 @@ function ModelPicker({ models, current, onChange }) {
|
|
|
2296
2299
|
] });
|
|
2297
2300
|
}
|
|
2298
2301
|
|
|
2302
|
+
// src/react/connectingGate.ts
|
|
2303
|
+
var CONNECTING_DELAY_MS = 1200;
|
|
2304
|
+
var CONNECTING_MIN_MS = 700;
|
|
2305
|
+
var initialGate = (now, connected) => ({ shownAt: null, waitingSince: connected ? null : now });
|
|
2306
|
+
function stepGate(g, connected, now, delay = CONNECTING_DELAY_MS, min = CONNECTING_MIN_MS) {
|
|
2307
|
+
const waitingSince = connected ? null : g.waitingSince ?? now;
|
|
2308
|
+
let shownAt = g.shownAt;
|
|
2309
|
+
if (shownAt === null && waitingSince !== null && now - waitingSince >= delay) shownAt = now;
|
|
2310
|
+
if (shownAt !== null && connected && now - shownAt >= min) shownAt = null;
|
|
2311
|
+
const gate = { shownAt, waitingSince };
|
|
2312
|
+
let recheckAt = null;
|
|
2313
|
+
if (shownAt === null && waitingSince !== null) recheckAt = waitingSince + delay;
|
|
2314
|
+
else if (shownAt !== null && connected) recheckAt = shownAt + min;
|
|
2315
|
+
return { gate, visible: shownAt !== null, recheckAt };
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2299
2318
|
// src/react/Status.tsx
|
|
2300
2319
|
import { AnimatePresence as AnimatePresence2, motion as motion2 } from "motion/react";
|
|
2301
2320
|
import { useEffect as useEffect4, useState as useState7 } from "react";
|
|
@@ -2657,10 +2676,10 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2657
2676
|
addReference: (r) => {
|
|
2658
2677
|
const current = refsRef.current;
|
|
2659
2678
|
if (!current.some((x) => x.id === r.id)) setRefs([...current, r]);
|
|
2660
|
-
requestAnimationFrame(() => composerBox.current?.querySelector("textarea")?.focus());
|
|
2679
|
+
requestAnimationFrame(() => composerBox.current?.querySelector("textarea")?.focus({ preventScroll: true }));
|
|
2661
2680
|
},
|
|
2662
2681
|
setDraft: (text) => setDraftState((d) => ({ text, n: d.n + 1 })),
|
|
2663
|
-
focus: () => composerBox.current?.querySelector("textarea")?.focus()
|
|
2682
|
+
focus: () => composerBox.current?.querySelector("textarea")?.focus({ preventScroll: true })
|
|
2664
2683
|
}),
|
|
2665
2684
|
[send, setRefs]
|
|
2666
2685
|
);
|
|
@@ -2763,14 +2782,23 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2763
2782
|
const pageInProgress = last?.role === "assistant" && (Boolean(last.planning) || (last.pages ?? []).some((p) => !p.error && (!p.readyAt || p.review?.status === "running")));
|
|
2764
2783
|
const toolRunning = busy && last?.role === "assistant" && ((last.tools ?? []).some(isRunning) || pageInProgress);
|
|
2765
2784
|
const empty = turns.length === 0 && !busy;
|
|
2766
|
-
const
|
|
2785
|
+
const perTurn = !external && transport?.stream === "per-turn";
|
|
2786
|
+
const ready = connected || perTurn;
|
|
2787
|
+
const placeholder = !ready ? t.placeholderConnecting : props.placeholder ?? (props.agentName ? t.placeholder(props.agentName) : t.placeholderDefault);
|
|
2767
2788
|
const showFiles = features.attachments ?? Boolean(props.upload);
|
|
2768
|
-
const
|
|
2789
|
+
const gateRef = useRef5(initialGate(Date.now(), ready));
|
|
2790
|
+
const [showConnecting, setShowConnecting] = useState9(false);
|
|
2769
2791
|
useEffect6(() => {
|
|
2770
|
-
|
|
2771
|
-
const
|
|
2792
|
+
let id;
|
|
2793
|
+
const tick = () => {
|
|
2794
|
+
const r = stepGate(gateRef.current, ready, Date.now());
|
|
2795
|
+
gateRef.current = r.gate;
|
|
2796
|
+
setShowConnecting(r.visible);
|
|
2797
|
+
if (r.recheckAt !== null) id = setTimeout(tick, Math.max(0, r.recheckAt - Date.now()));
|
|
2798
|
+
};
|
|
2799
|
+
tick();
|
|
2772
2800
|
return () => clearTimeout(id);
|
|
2773
|
-
}, [
|
|
2801
|
+
}, [ready]);
|
|
2774
2802
|
const pointedRefs = refs.filter((r) => r.prompt);
|
|
2775
2803
|
const plainRefs = refs.filter((r) => !r.prompt);
|
|
2776
2804
|
const removeRef = (id) => setRefs(refsRef.current.filter((r) => r.id !== id));
|
|
@@ -2807,7 +2835,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2807
2835
|
// empezar. Si no cabe (panel bajo), se desplaza: `safe center` nunca empuja nada fuera.
|
|
2808
2836
|
/* @__PURE__ */ jsxs8("div", { className: "gs-scroll flex min-h-0 flex-1 flex-col items-center justify-center overflow-y-auto px-4 pb-[10vh] [justify-content:safe_center]", children: [
|
|
2809
2837
|
slots.headerActions && /* @__PURE__ */ jsx10("div", { className: "absolute right-4 top-3 flex gap-2", children: slots.headerActions }),
|
|
2810
|
-
|
|
2838
|
+
/* @__PURE__ */ jsx10(AnimatePresence3, { mode: "wait", initial: false, children: showConnecting ? /* @__PURE__ */ jsx10(ConnectingState, { phase, error, avatar: props.avatar }, "connecting") : /* @__PURE__ */ jsx10("div", { children: slots.emptyState ?? /* @__PURE__ */ jsxs8("div", { className: "mb-6 flex flex-col items-center", children: [
|
|
2811
2839
|
props.avatar && /* @__PURE__ */ jsx10("div", { className: "mb-3", children: props.avatar }),
|
|
2812
2840
|
props.welcome && // Un saludo largo (una explicación del copiloto) baja a letra de lectura.
|
|
2813
2841
|
/* @__PURE__ */ jsx10(
|
|
@@ -2817,7 +2845,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2817
2845
|
children: props.welcome
|
|
2818
2846
|
}
|
|
2819
2847
|
)
|
|
2820
|
-
] }),
|
|
2848
|
+
] }) }, "welcome") }),
|
|
2821
2849
|
/* @__PURE__ */ jsxs8("div", { className: "w-full max-w-3xl", children: [
|
|
2822
2850
|
slots.aboveComposer,
|
|
2823
2851
|
composer,
|
package/dist/transport/index.js
CHANGED
|
@@ -23,6 +23,11 @@ export interface PilotTransport {
|
|
|
23
23
|
respondPermission?(id: string, optionId: string): Promise<{
|
|
24
24
|
resolved: boolean;
|
|
25
25
|
}>;
|
|
26
|
+
/**
|
|
27
|
+
* `"per-turn"`: no hay un stream vivo que esperar (cada turno trae el suyo, como el POST de
|
|
28
|
+
* EasyBits). Piloto pinta el hilo al instante y nunca enseña «conectando». Default `"live"`.
|
|
29
|
+
*/
|
|
30
|
+
stream?: "live" | "per-turn";
|
|
26
31
|
/** El hilo guardado, para pintarlo al montar (si la app no pasa `initialTurns`). */
|
|
27
32
|
history?(): Promise<Turn[] | PilotHistory>;
|
|
28
33
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghostykit",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Piloto UI
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Piloto UI — 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",
|
|
7
7
|
"types": "./dist/core/index.d.ts",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"@types/react": "^19.0.0",
|
|
63
63
|
"@types/react-dom": "^19.0.0",
|
|
64
64
|
"esbuild": "^0.25.0",
|
|
65
|
+
"jsdom": "^26.1.0",
|
|
65
66
|
"lucide-react": "^1.44.0",
|
|
66
67
|
"motion": "^12.38.0",
|
|
67
68
|
"postcss": "^8.5.0",
|