ghostykit 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,9 +3,10 @@ import {
3
3
  definePlugin,
4
4
  pluginPoints,
5
5
  pluginRequires
6
- } from "../chunks/chunk-VFCROLFF.js";
6
+ } from "../chunks/chunk-6PYYF4XU.js";
7
7
  import "../chunks/chunk-UUH37KXA.js";
8
8
  import "../chunks/chunk-6U545WJG.js";
9
+ import "../chunks/chunk-AQBQ3QZH.js";
9
10
 
10
11
  // src/plugins/index.tsx
11
12
  import { useEffect, useState, useSyncExternalStore } from "react";
@@ -31,47 +31,15 @@ import {
31
31
  readSse
32
32
  } from "../chunks/chunk-KG23PQI2.js";
33
33
  import {
34
- Activity,
35
- AppWindow,
36
- ArrowDown,
37
- ArrowUpRight,
38
34
  BUILTIN_CUSTOM,
39
- BookOpen,
40
- Check,
41
- ChevronRight,
42
- CircleCheck,
43
- CircleSlash,
44
- CircleX,
45
- Clock,
46
- Coins,
47
35
  Composer,
48
- Copy,
49
- ExternalLink,
50
- FilePenLine,
51
- FileSpreadsheet,
52
- FileText,
53
- Folder,
54
- Gauge,
55
- Globe,
56
- Image,
57
- Inbox,
58
- ListChecks,
59
- LoaderCircle,
60
36
  ModelPicker,
61
- OctagonX,
62
37
  PilotoContext,
63
38
  ReferenceChips,
64
- RotateCcw,
65
39
  STRINGS_EN,
66
40
  STRINGS_ES,
67
- Search,
68
- Send,
69
- ShieldAlert,
70
41
  Suggestions,
71
42
  TurnContext,
72
- UsersRound,
73
- Wrench,
74
- X,
75
43
  definePlugin,
76
44
  isDev,
77
45
  mimeOf,
@@ -80,7 +48,7 @@ import {
80
48
  resolvePlugins,
81
49
  rootProps,
82
50
  usePiloto
83
- } from "../chunks/chunk-VFCROLFF.js";
51
+ } from "../chunks/chunk-6PYYF4XU.js";
84
52
  import {
85
53
  TOOL_VERBS_EN,
86
54
  TOOL_VERBS_ES,
@@ -97,6 +65,40 @@ import {
97
65
  formatDuration,
98
66
  systemNoteOf
99
67
  } from "../chunks/chunk-6U545WJG.js";
68
+ import {
69
+ Activity,
70
+ AppWindow,
71
+ ArrowDown,
72
+ ArrowUpRight,
73
+ BookOpen,
74
+ Check,
75
+ ChevronRight,
76
+ CircleCheck,
77
+ CircleSlash,
78
+ CircleX,
79
+ Clock,
80
+ Coins,
81
+ Copy,
82
+ ExternalLink,
83
+ FilePenLine,
84
+ FileSpreadsheet,
85
+ FileText,
86
+ Folder,
87
+ Gauge,
88
+ Globe,
89
+ Image,
90
+ Inbox,
91
+ ListChecks,
92
+ LoaderCircle,
93
+ OctagonX,
94
+ RotateCcw,
95
+ Search,
96
+ Send,
97
+ ShieldAlert,
98
+ UsersRound,
99
+ Wrench,
100
+ X
101
+ } from "../chunks/chunk-AQBQ3QZH.js";
100
102
 
101
103
  // src/react/Piloto.tsx
102
104
  import {
@@ -9,6 +9,10 @@ export type PilotoSplitOptions = {
9
9
  desktopMinWidth?: number;
10
10
  /** Se llama cada vez que se abre o se cierra (p. ej. para contraer el menú lateral de la app). */
11
11
  onOpenChange?: (open: boolean) => void;
12
+ /** Atajo para abrir/cerrar con ⌘/Ctrl + esta tecla (default "i"; `false` lo apaga). */
13
+ shortcut?: string | false;
14
+ /** Esc cierra el panel de escritorio (default true; en la hoja lo maneja vaul). */
15
+ escapeToClose?: boolean;
12
16
  };
13
17
  export declare function usePilotoSplit(options: PilotoSplitOptions | string): {
14
18
  isDesktop: boolean;
@@ -71,3 +75,29 @@ export declare function PilotoLauncher({ split, label, avatarSrc, className, sty
71
75
  className?: string;
72
76
  style?: CSSProperties;
73
77
  }): import("react").JSX.Element;
78
+ /**
79
+ * La conversación visible: una llave que se guarda en el navegador y cambia con «Nueva conversación».
80
+ * La app arma con ella el `threadId` (p. ej. `${base}-${key}`); el hilo anterior NO se borra (sigue en
81
+ * el servidor), sólo deja de verse. Sin almacenamiento, arranca en "1".
82
+ */
83
+ export declare function useConversationKey(storageKey: string): readonly [string, () => void];
84
+ /**
85
+ * La carcasa estándar del piloto en el panel o la hoja: encabezado (avatar, nombre y acciones:
86
+ * «Nueva conversación» y cerrar) y el chat debajo. En el panel queda pegado a la ventana aunque la
87
+ * página tenga scroll (el composer siempre a la vista). Va dentro de `PilotoSplit` (`piloto={(v) => …}`).
88
+ */
89
+ export declare function PilotoPanel({ variant, title, avatar, onClose, onNewConversation, canStartNew, actions, locale, className, children, }: {
90
+ variant: PilotoSplitVariant;
91
+ title: ReactNode;
92
+ avatar?: ReactNode;
93
+ onClose?: () => void;
94
+ /** Sin esto no sale el botón. Normalmente `rotate` de `useConversationKey`. */
95
+ onNewConversation?: () => void;
96
+ /** Ocultar «Nueva conversación» (p. ej. con el hilo vacío). */
97
+ canStartNew?: boolean;
98
+ /** Más botones a la derecha del encabezado, antes de los estándar. */
99
+ actions?: ReactNode;
100
+ locale?: "es" | "en";
101
+ className?: string;
102
+ children: ReactNode;
103
+ }): import("react").JSX.Element;
@@ -1,3 +1,8 @@
1
+ import {
2
+ SquarePen,
3
+ X
4
+ } from "../chunks/chunk-AQBQ3QZH.js";
5
+
1
6
  // src/split/index.tsx
2
7
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
3
8
  import { Group, Panel, Separator, usePanelRef } from "react-resizable-panels";
@@ -7,7 +12,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
12
  var SHEET_SNAPS = [0.6, 0.94];
8
13
  var OPEN_MS = 320;
9
14
  function usePilotoSplit(options) {
10
- const { widthKey, defaultWidth = 420, desktopMinWidth = 1024, onOpenChange } = typeof options === "string" ? { widthKey: options } : options;
15
+ const { widthKey, defaultWidth = 420, desktopMinWidth = 1024, onOpenChange, shortcut = "i", escapeToClose = true } = typeof options === "string" ? { widthKey: options } : options;
11
16
  const isDesktop = useIsDesktop(desktopMinWidth);
12
17
  const panelRef = usePanelRef();
13
18
  const [panelOpen, setPanelOpen] = useState(false);
@@ -47,6 +52,21 @@ function usePilotoSplit(options) {
47
52
  useEffect(() => {
48
53
  notify.current?.(isOpen);
49
54
  }, [isOpen]);
55
+ const toggleRef = useRef(() => {
56
+ });
57
+ toggleRef.current = isOpen ? close : open;
58
+ useEffect(() => {
59
+ const onKey = (e) => {
60
+ if (shortcut && (e.metaKey || e.ctrlKey) && !e.altKey && e.key.toLowerCase() === shortcut.toLowerCase()) {
61
+ e.preventDefault();
62
+ toggleRef.current();
63
+ } else if (escapeToClose && e.key === "Escape" && !e.defaultPrevented && isDesktop && isOpen) {
64
+ close();
65
+ }
66
+ };
67
+ window.addEventListener("keydown", onKey);
68
+ return () => window.removeEventListener("keydown", onKey);
69
+ }, [shortcut, escapeToClose, isDesktop, isOpen, close]);
50
70
  return {
51
71
  isDesktop,
52
72
  panelRef,
@@ -230,9 +250,71 @@ function PilotoLauncher({
230
250
  "gk-launcher"
231
251
  ) });
232
252
  }
253
+ function useConversationKey(storageKey) {
254
+ const [key, setKey] = useState(() => {
255
+ try {
256
+ return localStorage.getItem(storageKey) || "1";
257
+ } catch {
258
+ return "1";
259
+ }
260
+ });
261
+ const rotate = useCallback(() => {
262
+ const next = Date.now().toString(36);
263
+ try {
264
+ localStorage.setItem(storageKey, next);
265
+ } catch {
266
+ }
267
+ setKey(next);
268
+ }, [storageKey]);
269
+ return [key, rotate];
270
+ }
271
+ var PANEL_LABELS = {
272
+ es: { newConversation: "Nueva conversaci\xF3n", close: "Cerrar" },
273
+ en: { newConversation: "New conversation", close: "Close" }
274
+ };
275
+ function PilotoPanel({
276
+ variant,
277
+ title,
278
+ avatar,
279
+ onClose,
280
+ onNewConversation,
281
+ canStartNew = true,
282
+ actions,
283
+ locale = "es",
284
+ className = "",
285
+ children
286
+ }) {
287
+ const sticky = useStickyPanelHeight(variant === "panel");
288
+ const t = PANEL_LABELS[locale];
289
+ return /* @__PURE__ */ jsxs(
290
+ "aside",
291
+ {
292
+ ref: sticky.ref,
293
+ "aria-label": typeof title === "string" ? title : void 0,
294
+ style: sticky.style,
295
+ className: `gk-panel ${variant === "panel" ? "gk-panel-side" : ""} ${className}`,
296
+ children: [
297
+ /* @__PURE__ */ jsxs("header", { className: "gk-panel-header", children: [
298
+ /* @__PURE__ */ jsxs("div", { className: "gk-panel-title", children: [
299
+ avatar,
300
+ /* @__PURE__ */ jsx("span", { children: title })
301
+ ] }),
302
+ /* @__PURE__ */ jsxs("div", { className: "gk-panel-actions", children: [
303
+ actions,
304
+ onNewConversation && canStartNew && /* @__PURE__ */ jsx("button", { type: "button", className: "gk-panel-btn", onClick: onNewConversation, title: t.newConversation, "aria-label": t.newConversation, children: /* @__PURE__ */ jsx(SquarePen, { size: 17, strokeWidth: 1.8 }) }),
305
+ onClose && variant === "panel" && /* @__PURE__ */ jsx("button", { type: "button", className: "gk-panel-btn", onClick: onClose, title: t.close, "aria-label": t.close, children: /* @__PURE__ */ jsx(X, { size: 18, strokeWidth: 1.8 }) })
306
+ ] })
307
+ ] }),
308
+ /* @__PURE__ */ jsx("div", { className: "gk-panel-body", children })
309
+ ]
310
+ }
311
+ );
312
+ }
233
313
  export {
234
314
  PilotoLauncher,
315
+ PilotoPanel,
235
316
  PilotoSplit,
317
+ useConversationKey,
236
318
  useIsDesktop,
237
319
  usePilotoSplit,
238
320
  useStickyPanelHeight
package/dist/split.css CHANGED
@@ -69,3 +69,15 @@
69
69
  }
70
70
  .gk-launcher img { width: 32px; height: 32px; object-fit: contain; }
71
71
  .gk-launcher-icon { padding: 8px; }
72
+
73
+ /* Carcasa estándar del piloto (PilotoPanel). Colores: --gk-panel-bg, --gk-panel-border, --gk-panel-fg, --gk-panel-muted, --gk-panel-hover. */
74
+ .gk-panel { display: flex; width: 100%; flex-direction: column; overflow: hidden; background: var(--gk-panel-bg, #fff); }
75
+ .gk-panel-side { border-left: 1px solid var(--gk-panel-border, #e5e7eb); }
76
+ .gk-panel-header { display: flex; flex-shrink: 0; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 12px 10px 16px; border-bottom: 1px solid var(--gk-panel-border, #eceef2); }
77
+ .gk-panel-title { display: flex; min-width: 0; align-items: center; gap: 8px; color: var(--gk-panel-fg, #111827); font-size: 14px; font-weight: 600; }
78
+ .gk-panel-title > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
79
+ .gk-panel-title img { width: 24px; height: 24px; object-fit: contain; flex-shrink: 0; }
80
+ .gk-panel-actions { display: flex; align-items: center; gap: 2px; }
81
+ .gk-panel-btn { display: inline-flex; width: 32px; height: 32px; align-items: center; justify-content: center; border: 0; border-radius: 9999px; background: transparent; color: var(--gk-panel-muted, #6b7280); cursor: pointer; }
82
+ .gk-panel-btn:hover { background: var(--gk-panel-hover, rgb(17 24 39 / 0.06)); color: var(--gk-panel-fg, #111827); }
83
+ .gk-panel-body { display: flex; min-height: 0; flex: 1; flex-direction: column; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostykit",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Ghosty Autopilot — the autopilot chat for your React app, on AG-UI: works with LangGraph, Mastra, CrewAI or any AG-UI agent. Streaming text, agent steps, human approval cards (interrupt/resume), attachments and voice.",
5
5
  "type": "module",
6
6
  "main": "./dist/core/index.js",