ghostykit 0.2.1 → 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.
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Piloto UI · `ghostykit`
2
2
 
3
3
  **An in-app copilot for React.** Piloto UI is the agent chat from
4
- [Ghosty Studio](https://www.ghosty.studio), packaged as a library. You drop it into an app you already
4
+ [Ghosty Studio](https://www.ghosty.studio) (`/c`), packaged as a library: same components, same
5
+ markup, same look ([side by side](https://raw.githubusercontent.com/blissito/ghostykit/main/docs/screenshots/parity/desktop-done-gs-vs-ghostykit.png)). You drop it into an app you already
5
6
  have. It sees what the user has on screen, streams what the agent says and does, and **stops to ask
6
7
  before anything risky** (human in the loop).
7
8
 
@@ -131,9 +132,11 @@ your own layout.
131
132
  ### Skins and theming
132
133
 
133
134
  `ghostykit/styles.css` is self-contained and scoped under `.piloto`. It works the same with
134
- **Tailwind 3, Tailwind 4 or no Tailwind** (verified in a Tailwind 3 app: its utilities compute
135
- identically with and without Piloto's CSS). Every color, radius, border width and shadow is a
136
- variable:
135
+ **Tailwind 3, Tailwind 4 or no Tailwind**, and it shields its fields from the host's CSS (verified
136
+ in a Tailwind 3 app **with `@tailwindcss/forms`**: Piloto's textarea gets no border or focus ring, and
137
+ the app's own utilities compute identically with and without Piloto's CSS). Sizes don't depend on
138
+ the host's `html` font size either: `1rem` inside Piloto is `--piloto-rem` (18 px in the Ghosty skin,
139
+ like ghosty.studio; 16 px in the others). Every color, radius, border width and shadow is a variable:
137
140
 
138
141
  ```css
139
142
  .my-app .piloto {
@@ -142,6 +145,7 @@ variable:
142
145
  --piloto-radius-card: 6px;
143
146
  --piloto-border-width: 3px;
144
147
  --piloto-shadow-hero: 6px 6px 0 #0f766e;
148
+ --piloto-rem: 16px;
145
149
  --piloto-font: "Your Sans", sans-serif;
146
150
  }
147
151
  ```
@@ -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);
package/dist/fonts.css CHANGED
@@ -1,3 +1,3 @@
1
- /* Opcional: las fuentes de las pieles de casa (Schibsted Grotesk para leer, Big Shoulders Stencil
2
- para el reloj y el sello del pase). Sin esto, Piloto cae a las fuentes del sistema. */
3
- @import url("https://fonts.googleapis.com/css2?family=Big+Shoulders+Stencil+Display:wght@700;800;900&family=Schibsted+Grotesk:ital,wght@0,400..900;1,400..900&display=swap");
1
+ /* Opcional: las fuentes de las pieles de casa — Inter (la de Ghosty) y Big Shoulders Stencil (el
2
+ reloj y el sello del pase de permiso). Sin esto, Piloto cae a las fuentes del sistema. */
3
+ @import url("https://fonts.googleapis.com/css2?family=Big+Shoulders+Stencil+Display:wght@800;900&family=Inter:opsz,wght@14..32,400..800&display=swap");
@@ -16,11 +16,12 @@ export declare function ReplyOptionButtons({ options, onPick }: {
16
16
  /** Tokens del turno, qué tanto del contexto se llevó y cuánto costó. */
17
17
  export declare function UsageStats({ used, size, cost }: Usage): import("react").JSX.Element | null;
18
18
  /** El pie de una respuesta terminada: cuánto tardó, copiar y reintentar. */
19
- export declare function TurnFooter({ text, seconds, onRetry, showCopy }: {
19
+ export declare function TurnFooter({ text, seconds, onRetry, showCopy, showDuration }: {
20
20
  text: string;
21
21
  seconds?: number;
22
22
  onRetry?: () => void;
23
23
  showCopy?: boolean;
24
+ showDuration?: boolean;
24
25
  }): import("react").JSX.Element;
25
26
  /** Las entregas por defecto: una ficha por archivo (las apps pintan las suyas con `renderDeliveries`). */
26
27
  export declare function DeliveryChips({ deliveries }: {
@@ -35,7 +36,8 @@ export interface AgentBubbleProps {
35
36
  onOption?: (text: string) => void;
36
37
  showUsage?: boolean;
37
38
  showCopy?: boolean;
39
+ showDuration?: boolean;
38
40
  /** Las entregas (y lo que la app quiera bajo la respuesta). Sin esto, fichas simples. */
39
41
  renderDeliveries?: (turn: Turn, live: boolean) => ReactNode;
40
42
  }
41
- export declare function AgentBubble({ turn, live, onRetry, onOption, showUsage, showCopy, renderDeliveries }: AgentBubbleProps): import("react").JSX.Element;
43
+ export declare function AgentBubble({ turn, live, onRetry, onOption, showUsage, showCopy, showDuration, renderDeliveries }: AgentBubbleProps): import("react").JSX.Element;
@@ -1,13 +1,17 @@
1
1
  import type { Reference } from "../core/types";
2
- /** Las referencias del mensaje: chips atómicos con ×. Clic en el chip = `onActivate` (resaltar en la app). */
3
- export declare function ReferenceChips({ references, onRemove, onActivate, }: {
2
+ /**
3
+ * Las referencias del mensaje: chips atómicos con ×. Una con `prompt` (un elemento señalado) se pinta
4
+ * con la mira, como en Ghosty; las demás (tarjetas del tablero), con su etiqueta. Clic = `onActivate`.
5
+ */
6
+ export declare function ReferenceChips({ references, onRemove, onActivate }: {
4
7
  references: Reference[];
5
8
  onRemove: (id: string) => void;
6
9
  onActivate?: (ref: Reference) => void;
7
10
  }): import("react").JSX.Element | null;
8
11
  /** Sugerencias para arrancar. Una que termina en «: » se escribe en la caja en vez de mandarse. */
9
- export declare function Suggestions({ items, onPick, onPrefill }: {
12
+ export declare function Suggestions({ items, onPick, onPrefill, disabled }: {
10
13
  items: string[];
11
14
  onPick: (text: string) => void;
12
15
  onPrefill?: (text: string) => void;
16
+ disabled?: boolean;
13
17
  }): import("react").JSX.Element | null;
@@ -37,7 +37,10 @@ export interface ComposerProps {
37
37
  maxFileMb?: number;
38
38
  }
39
39
  export declare function Composer({ onSubmit, onStop, busy, canSteer, placeholder, initialValue, autoFocus, canSeeImages, attachments: withFiles, upload, transcribe, voiceNoteText, models, currentModel, onModelChange, references, onRemoveReference, onActivateReference, extras, maxFiles, maxFileMb, }: ComposerProps): import("react").JSX.Element;
40
- /** El selector de modelo: un menú corto hacia arriba, sin Radix. Cierra con clic fuera y Escape. */
40
+ /**
41
+ * El selector de modelo. PORTADO de Ghosty (`ModelPicker.tsx`): dropdown discreto hacia arriba, sin
42
+ * Radix; cierra con clic fuera y con Escape.
43
+ */
41
44
  export declare function ModelPicker({ models, current, onChange }: {
42
45
  models: ModelOption[];
43
46
  current: string | null;
@@ -1,6 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { ConnectPhase } from "../core/types";
3
- /** Los tres puntos de «escribiendo». Color y animación por CSS (`.piloto-typing`). */
3
+ /** Los tres puntitos de «escribiendo» (color y animación en CSS: `gc-typing`). */
4
4
  export declare function TypingDots(): import("react").JSX.Element;
5
5
  export declare function WaitLabel({ phase, since, elsewhere, onLost, showDuration, }: {
6
6
  phase: ConnectPhase;
@@ -1,22 +1,27 @@
1
- import { type ReactNode } from "react";
2
1
  import type { ToolEntry } from "../core/types";
3
2
  import { type PilotIcon } from "./context";
4
- /** La garra de máquina de peluches: el ícono de «usó una herramienta» que no tiene clase propia. */
3
+ /** La garra de máquina de peluches: el ícono de «usó una herramienta» (baja, cierra y sube en bucle). */
5
4
  export declare function ClawIcon({ className }: {
6
5
  className?: string;
7
6
  }): import("react").JSX.Element;
8
- /** Íconos de casa por `kind`. Lo desconocido cae en la garra. */
7
+ /** Brazo robot agarrando — «Robot grab» de Delapouite (game-icons.net, CC BY 3.0): los pasos técnicos. */
8
+ export declare function RobotArmIcon({ className }: {
9
+ className?: string;
10
+ }): import("react").JSX.Element;
11
+ /** Máquina arcade (Iconoir «arcade», MIT): las herramientas de un MCP conectado. */
12
+ export declare function ArcadeIcon({ className }: {
13
+ className?: string;
14
+ }): import("react").JSX.Element;
15
+ /** Un ícono por tipo de herramienta (los de Ghosty); lo desconocido cae en la garra. */
9
16
  export declare const DEFAULT_TOOL_ICONS: Record<string, PilotIcon>;
10
17
  export declare function AgentSteps({ tools, live }: {
11
18
  tools: ToolEntry[];
12
19
  live: boolean;
13
20
  }): import("react").JSX.Element;
14
- /** El detalle de un paso (lista desplegada): rótulo, detalle y ruta. */
15
21
  export declare function ToolRow({ tool }: {
16
22
  tool: ToolEntry;
17
23
  }): import("react").JSX.Element;
18
24
  /** La narración del agente como avance: una palomita por lo que ya dijo que hacía. */
19
- export declare function Narration({ steps, children }: {
25
+ export declare function Narration({ steps }: {
20
26
  steps: string[];
21
- children?: ReactNode;
22
27
  }): import("react").JSX.Element;
@@ -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
+ };