ghostykit 0.3.0 → 0.3.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.
@@ -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
+ };
@@ -35,7 +35,7 @@ import {
35
35
  createPostStreamTransport,
36
36
  createSseTransport,
37
37
  readSse
38
- } from "../chunks/chunk-D6JXJWYM.js";
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
 
@@ -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";
@@ -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 placeholder = !connected ? t.placeholderConnecting : props.placeholder ?? (props.agentName ? t.placeholder(props.agentName) : t.placeholderDefault);
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 [slowConnect, setSlowConnect] = useState9(false);
2789
+ const gateRef = useRef5(initialGate(Date.now(), ready));
2790
+ const [showConnecting, setShowConnecting] = useState9(false);
2769
2791
  useEffect6(() => {
2770
- if (connected) return setSlowConnect(false);
2771
- const id = setTimeout(() => setSlowConnect(true), 800);
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
- }, [connected]);
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
- !connected && slowConnect ? /* @__PURE__ */ jsx10(ConnectingState, { phase, error, avatar: props.avatar }) : slots.emptyState ?? /* @__PURE__ */ jsxs8("div", { className: "mb-6 flex flex-col items-center", children: [
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,
@@ -7,7 +7,7 @@ import {
7
7
  createPostStreamTransport,
8
8
  createSseTransport,
9
9
  readSse
10
- } from "../chunks/chunk-D6JXJWYM.js";
10
+ } from "../chunks/chunk-XZHX2NZT.js";
11
11
  export {
12
12
  MIN_SILENCE_MS,
13
13
  MISSED_BEATS,
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostykit",
3
- "version": "0.3.0",
3
+ "version": "0.3.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",