cursor-buddy 0.0.10 → 0.0.11

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.
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { c as CursorRenderProps, d as SpeechBubbleRenderProps, i as CursorBuddyMediaMode, m as WaveformRenderProps, o as CursorBuddySpeechConfig, p as VoiceState, r as CursorBuddyClientOptions, s as CursorBuddyTranscriptionConfig, u as PointingTarget } from "../client-sjVVGYPU.mjs";
2
+ import { _ as ToolCallState, b as ToolDisplayOptions, c as CursorRenderProps, d as SpeechBubbleRenderProps, g as ToolCallEvent, h as ToolBubbleRenderProps, m as WaveformRenderProps, n as CursorBuddyClientOptions, o as CursorBuddySpeechConfig, p as VoiceState, r as CursorBuddyMediaMode, s as CursorBuddyTranscriptionConfig, u as PointingTarget, v as ToolCallStatus, x as ToolResultEvent, y as ToolDisplayConfig } from "../types-BU0Gegg2.mjs";
3
3
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
4
4
 
5
5
  //#region src/react/components/Overlay.d.ts
@@ -10,12 +10,16 @@ interface OverlayProps {
10
10
  speechBubble?: (props: SpeechBubbleRenderProps) => React.ReactNode;
11
11
  /** Custom waveform renderer */
12
12
  waveform?: (props: WaveformRenderProps) => React.ReactNode;
13
+ /** Tool display configuration */
14
+ toolDisplay?: ToolDisplayConfig;
15
+ /** Custom tool bubble renderer (overrides per-tool config) */
16
+ renderToolBubble?: (props: ToolBubbleRenderProps) => React.ReactNode;
13
17
  /** Container element for portal (defaults to document.body) */
14
18
  container?: HTMLElement | null;
15
19
  }
16
20
  //#endregion
17
21
  //#region src/react/components/CursorBuddy.d.ts
18
- interface CursorBuddyProps extends Pick<OverlayProps, "cursor" | "speechBubble" | "waveform"> {
22
+ interface CursorBuddyProps extends Pick<OverlayProps, "cursor" | "speechBubble" | "waveform" | "toolDisplay" | "renderToolBubble"> {
19
23
  /** API endpoint for cursor buddy server */
20
24
  endpoint: string;
21
25
  /** Hotkey for push-to-talk (default: "ctrl+alt") */
@@ -36,6 +40,10 @@ interface CursorBuddyProps extends Pick<OverlayProps, "cursor" | "speechBubble"
36
40
  onStateChange?: (state: VoiceState) => void;
37
41
  /** Callback when error occurs */
38
42
  onError?: (error: Error) => void;
43
+ /** Callback when a tool is called */
44
+ onToolCall?: (event: ToolCallEvent) => void;
45
+ /** Callback when a tool completes */
46
+ onToolResult?: (event: ToolResultEvent) => void;
39
47
  }
40
48
  /**
41
49
  * Drop-in cursor buddy component.
@@ -69,11 +77,15 @@ declare function CursorBuddy({
69
77
  cursor,
70
78
  speechBubble,
71
79
  waveform,
80
+ toolDisplay,
81
+ renderToolBubble,
72
82
  onTranscript,
73
83
  onResponse,
74
84
  onPoint,
75
85
  onStateChange,
76
- onError
86
+ onError,
87
+ onToolCall,
88
+ onToolResult
77
89
  }: CursorBuddyProps): _$react_jsx_runtime0.JSX.Element;
78
90
  //#endregion
79
91
  //#region src/react/hooks.d.ts
@@ -84,7 +96,7 @@ interface UseCursorBuddyReturn {
84
96
  liveTranscript: string;
85
97
  /** Latest transcribed user speech */
86
98
  transcript: string;
87
- /** Latest AI response (stripped of POINT tags) */
99
+ /** Latest AI response */
88
100
  response: string;
89
101
  /** Current audio level (0-1) */
90
102
  audioLevel: number;
@@ -94,6 +106,12 @@ interface UseCursorBuddyReturn {
94
106
  isPointing: boolean;
95
107
  /** Current error (null if none) */
96
108
  error: Error | null;
109
+ /** All tool calls in current turn */
110
+ toolCalls: ToolCallState[];
111
+ /** Visible, non-expired tool calls */
112
+ activeToolCalls: ToolCallState[];
113
+ /** Tool awaiting user approval, or null */
114
+ pendingApproval: ToolCallState | null;
97
115
  /** Start listening (called automatically by hotkey) */
98
116
  startListening: () => void;
99
117
  /** Stop listening and process (called automatically by hotkey release) */
@@ -106,6 +124,12 @@ interface UseCursorBuddyReturn {
106
124
  dismissPointing: () => void;
107
125
  /** Reset to idle state */
108
126
  reset: () => void;
127
+ /** Approve a pending tool call */
128
+ approveToolCall: (id: string) => void;
129
+ /** Deny a pending tool call */
130
+ denyToolCall: (id: string) => void;
131
+ /** Dismiss a tool call bubble manually */
132
+ dismissToolCall: (id: string) => void;
109
133
  }
110
134
  /**
111
135
  * Hook to access cursor buddy state and actions.
@@ -130,13 +154,66 @@ declare function CursorBuddyProvider({
130
154
  endpoint,
131
155
  transcription,
132
156
  speech,
157
+ toolDisplay,
133
158
  children,
134
159
  onTranscript,
135
160
  onResponse,
136
161
  onPoint,
137
162
  onStateChange,
138
- onError
163
+ onError,
164
+ onToolCall,
165
+ onToolResult
139
166
  }: CursorBuddyProviderProps): _$react_jsx_runtime0.JSX.Element;
140
167
  //#endregion
141
- export { CursorBuddy, type CursorBuddyMediaMode, type CursorBuddyProps, CursorBuddyProvider, type CursorBuddyProviderProps, type CursorBuddySpeechConfig, type CursorBuddyTranscriptionConfig, type CursorRenderProps, type SpeechBubbleRenderProps, type UseCursorBuddyReturn, type WaveformRenderProps, useCursorBuddy };
168
+ //#region src/react/components/ToolBubble.d.ts
169
+ interface ToolBubbleProps extends ToolBubbleRenderProps {
170
+ /** Custom render function (from toolDisplay config) */
171
+ customRender?: (props: ToolBubbleRenderProps) => React.ReactNode;
172
+ }
173
+ /**
174
+ * Default tool bubble component.
175
+ * Displays tool call status with optional approve/deny buttons.
176
+ */
177
+ declare function ToolBubble({
178
+ toolName,
179
+ args,
180
+ status,
181
+ label,
182
+ result,
183
+ error,
184
+ approve,
185
+ deny,
186
+ dismiss,
187
+ customRender
188
+ }: ToolBubbleProps): _$react_jsx_runtime0.JSX.Element;
189
+ //#endregion
190
+ //#region src/react/components/ToolBubbleStack.d.ts
191
+ interface ToolBubbleStackProps {
192
+ /** Active tool calls to display */
193
+ toolCalls: ToolCallState[];
194
+ /** Tool display configuration */
195
+ toolDisplay?: ToolDisplayConfig;
196
+ /** Approve a tool call */
197
+ onApprove: (id: string) => void;
198
+ /** Deny a tool call */
199
+ onDeny: (id: string) => void;
200
+ /** Dismiss a tool call bubble */
201
+ onDismiss: (id: string) => void;
202
+ /** Custom render function for all bubbles (overrides per-tool config) */
203
+ renderToolBubble?: (props: ToolBubbleRenderProps) => React.ReactNode;
204
+ }
205
+ /**
206
+ * Stack of tool bubbles displayed near the cursor.
207
+ * Renders all active tool calls in a vertical column.
208
+ */
209
+ declare function ToolBubbleStack({
210
+ toolCalls,
211
+ toolDisplay,
212
+ onApprove,
213
+ onDeny,
214
+ onDismiss,
215
+ renderToolBubble
216
+ }: ToolBubbleStackProps): _$react_jsx_runtime0.JSX.Element | null;
217
+ //#endregion
218
+ export { CursorBuddy, type CursorBuddyMediaMode, type CursorBuddyProps, CursorBuddyProvider, type CursorBuddyProviderProps, type CursorBuddySpeechConfig, type CursorBuddyTranscriptionConfig, type CursorRenderProps, type SpeechBubbleRenderProps, ToolBubble, type ToolBubbleProps, type ToolBubbleRenderProps, ToolBubbleStack, type ToolBubbleStackProps, type ToolCallEvent, type ToolCallState, type ToolCallStatus, type ToolDisplayConfig, type ToolDisplayOptions, type ToolResultEvent, type UseCursorBuddyReturn, type WaveformRenderProps, useCursorBuddy };
142
219
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/react/components/Overlay.tsx","../../src/react/components/CursorBuddy.tsx","../../src/react/hooks.ts","../../src/react/provider.tsx"],"mappings":";;;;UAsBiB,YAAA;;EAEf,MAAA,GAAS,KAAA,CAAM,SAAA,KAAc,KAAA,EAAO,iBAAA,KAAsB,KAAA,CAAM,SAAA;;EAEhE,YAAA,IAAgB,KAAA,EAAO,uBAAA,KAA4B,KAAA,CAAM,SAAA;EAJ9B;EAM3B,QAAA,IAAY,KAAA,EAAO,mBAAA,KAAwB,KAAA,CAAM,SAAA;EAJxC;EAMT,SAAA,GAAY,WAAA;AAAA;;;UCjBG,gBAAA,SACP,IAAA,CAAK,YAAA;;EAEb,QAAA;EDMe;ECJf,MAAA;;EAEA,SAAA,GAAY,WAAA;EDIwB;ECFpC,aAAA,GAAgB,8BAAA;EDIO;ECFvB,MAAA,GAAS,uBAAA;EDIU;ECFnB,YAAA,IAAgB,IAAA;EDIJ;ECFZ,UAAA,IAAc,IAAA;EDES;ECAvB,OAAA,IAAW,MAAA,EAAQ,cAAA;EDNV;ECQT,aAAA,IAAiB,KAAA,EAAO,UAAA;EDRY;ECUpC,OAAA,IAAW,KAAA,EAAO,KAAA;AAAA;;;;;;;;;;;;;;;;;;;AArBpB;;;;;iBA2EgB,WAAA,CAAA;EACd,QAAA;EACA,MAAA;EACA,SAAA;EACA,MAAA;EACA,aAAA;EACA,MAAA;EACA,YAAA;EACA,QAAA;EACA,YAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;EACA;AAAA,GACC,gBAAA,GAAgB,oBAAA,CAAA,GAAA,CAAA,OAAA;;;UC9FF,oBAAA;;EAEf,KAAA,EAAO,UAAA;;EAEP,cAAA;EFU2B;EER3B,UAAA;EFUS;EERT,QAAA;EFQ0D;EEN1D,UAAA;EFQmD;EENnD,SAAA;EFQ2C;EEN3C,UAAA;EFQuB;EENvB,KAAA,EAAO,KAAA;EFAP;EEGA,cAAA;EFHe;EEKf,aAAA;EFL6B;EEO7B,UAAA,GAAa,OAAA;EFPmD;EEShE,OAAA,GAAU,CAAA,UAAW,CAAA,UAAW,KAAA;EFPT;EESvB,eAAA;EFTmD;EEWnD,KAAA;AAAA;;;;iBAMc,cAAA,CAAA,GAAkB,oBAAA;;;UC7BjB,wBAAA,SAAiC,wBAAA;;EAEhD,QAAA;EHMe;EGJf,aAAA,GAAgB,8BAAA;;EAEhB,MAAA,GAAS,uBAAA;EHI2B;EGFpC,QAAA,EAAU,KAAA,CAAM,SAAA;AAAA;;;;iBAMF,mBAAA,CAAA;EACd,QAAA;EACA,aAAA;EACA,MAAA;EACA,QAAA;EACA,YAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;EACA;AAAA,GACC,wBAAA,GAAwB,oBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/react/components/Overlay.tsx","../../src/react/components/CursorBuddy.tsx","../../src/react/hooks.ts","../../src/react/provider.tsx","../../src/react/components/ToolBubble.tsx","../../src/react/components/ToolBubbleStack.tsx"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { a as $buddyScale, i as $buddyRotation, n as $audioLevel, o as $cursorPosition, r as $buddyPosition, s as $pointingTarget, t as CursorBuddyClient } from "../client-CliXcNch.mjs";
2
+ import { a as $buddyScale, i as $buddyRotation, n as $audioLevel, o as $cursorPosition, r as $buddyPosition, s as $pointingTarget, t as CursorBuddyClient } from "../client-D7kFGsuH.mjs";
3
3
  import { useStore } from "@nanostores/react";
4
4
  import { createContext, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from "react";
5
- import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { createPortal } from "react-dom";
7
7
  //#region src/react/styles.css?inline
8
- var styles_default = "/**\n * Cursor Buddy Styles\n *\n * Customize by overriding CSS variables in your own stylesheet:\n *\n * :root {\n * --cursor-buddy-color-idle: #8b5cf6;\n * }\n */\n\n:root {\n /* Cursor colors by state */\n --cursor-buddy-color-idle: #3b82f6;\n --cursor-buddy-color-listening: #ef4444;\n --cursor-buddy-color-processing: #eab308;\n --cursor-buddy-color-responding: #22c55e;\n --cursor-buddy-cursor-stroke: #ffffff;\n --cursor-buddy-cursor-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n\n /* Speech bubble */\n --cursor-buddy-bubble-bg: #ffffff;\n --cursor-buddy-bubble-text: #1f2937;\n --cursor-buddy-bubble-radius: 8px;\n --cursor-buddy-bubble-padding: 8px 12px;\n --cursor-buddy-bubble-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n --cursor-buddy-bubble-max-width: 200px;\n --cursor-buddy-bubble-font-size: 14px;\n\n /* Waveform */\n --cursor-buddy-waveform-color: #ef4444;\n --cursor-buddy-waveform-bar-width: 4px;\n --cursor-buddy-waveform-bar-radius: 2px;\n --cursor-buddy-waveform-gap: 3px;\n\n /* Overlay */\n --cursor-buddy-z-index: 2147483647;\n\n /* Animation durations */\n --cursor-buddy-transition-fast: 0.1s;\n --cursor-buddy-transition-normal: 0.2s;\n --cursor-buddy-animation-duration: 0.3s;\n}\n\n/* Overlay container */\n.cursor-buddy-overlay {\n position: fixed;\n inset: 0;\n pointer-events: none;\n isolation: isolate;\n z-index: var(--cursor-buddy-z-index);\n}\n\n/* Buddy container (cursor + accessories)\n Positioned at the $buddyPosition coordinates (cursor center).\n CSS transform shifts the cursor to the right of the mouse pointer\n to avoid overlapping with the system cursor.\n Customize this offset via CSS to change cursor positioning.\n*/\n.cursor-buddy-container {\n position: absolute;\n transform: translate(8px, 4px);\n}\n\n/* Cursor SVG */\n.cursor-buddy-cursor {\n transition: transform var(--cursor-buddy-transition-fast) ease-out;\n filter: drop-shadow(var(--cursor-buddy-cursor-shadow));\n}\n\n.cursor-buddy-cursor polygon {\n stroke: var(--cursor-buddy-cursor-stroke);\n stroke-width: 2;\n transition: fill var(--cursor-buddy-transition-normal) ease-out;\n}\n\n.cursor-buddy-cursor--idle polygon {\n fill: var(--cursor-buddy-color-idle);\n}\n\n.cursor-buddy-cursor--listening polygon {\n fill: var(--cursor-buddy-color-listening);\n}\n\n.cursor-buddy-cursor--processing polygon {\n fill: var(--cursor-buddy-color-processing);\n}\n\n.cursor-buddy-cursor--responding polygon {\n fill: var(--cursor-buddy-color-responding);\n}\n\n/* Processing spinner */\n.cursor-buddy-cursor__spinner {\n color: var(--cursor-buddy-color-processing);\n}\n\n/* Cursor pulse animation during listening */\n.cursor-buddy-cursor--listening {\n animation: cursor-buddy-pulse 1.5s ease-in-out infinite;\n}\n\n@keyframes cursor-buddy-pulse {\n 0%,\n 100% {\n filter: drop-shadow(var(--cursor-buddy-cursor-shadow));\n }\n 50% {\n filter: drop-shadow(0 0 8px var(--cursor-buddy-color-listening));\n }\n}\n\n/* Processing spinner effect */\n.cursor-buddy-cursor--processing {\n animation: cursor-buddy-spin-subtle 2s linear infinite;\n}\n\n@keyframes cursor-buddy-spin-subtle {\n 0% {\n filter: drop-shadow(var(--cursor-buddy-cursor-shadow)) hue-rotate(0deg);\n }\n 100% {\n filter: drop-shadow(var(--cursor-buddy-cursor-shadow)) hue-rotate(360deg);\n }\n}\n\n/* Speech bubble */\n.cursor-buddy-bubble {\n position: absolute;\n left: 24px;\n top: -8px;\n pointer-events: auto;\n cursor: pointer;\n max-width: var(--cursor-buddy-bubble-max-width);\n padding: var(--cursor-buddy-bubble-padding);\n background-color: var(--cursor-buddy-bubble-bg);\n color: var(--cursor-buddy-bubble-text);\n border-radius: var(--cursor-buddy-bubble-radius);\n box-shadow: var(--cursor-buddy-bubble-shadow);\n font-size: var(--cursor-buddy-bubble-font-size);\n line-height: 1.4;\n width: max-content;\n overflow-wrap: break-word;\n word-break: break-word;\n user-select: none;\n animation: cursor-buddy-fade-in var(--cursor-buddy-animation-duration)\n ease-out;\n}\n\n@keyframes cursor-buddy-fade-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Waveform container */\n.cursor-buddy-waveform {\n position: absolute;\n left: 24px;\n top: 2px;\n display: flex;\n align-items: center;\n gap: var(--cursor-buddy-waveform-gap);\n height: 24px;\n animation: cursor-buddy-fade-in var(--cursor-buddy-animation-duration)\n ease-out;\n}\n\n/* Waveform bars */\n.cursor-buddy-waveform-bar {\n width: var(--cursor-buddy-waveform-bar-width);\n background-color: var(--cursor-buddy-waveform-color);\n border-radius: var(--cursor-buddy-waveform-bar-radius);\n transition: height 0.05s ease-out;\n}\n\n/* Fade out animation (applied via JS) */\n.cursor-buddy-fade-out {\n animation: cursor-buddy-fade-out var(--cursor-buddy-animation-duration)\n ease-out forwards;\n}\n\n@keyframes cursor-buddy-fade-out {\n from {\n opacity: 1;\n }\n to {\n opacity: 0;\n }\n}\n";
8
+ var styles_default = ":root{--cursor-buddy-color-idle:#3b82f6;--cursor-buddy-color-listening:#ef4444;--cursor-buddy-color-processing:#eab308;--cursor-buddy-color-responding:#22c55e;--cursor-buddy-cursor-stroke:#fff;--cursor-buddy-cursor-shadow:0 2px 4px #0000004d;--cursor-buddy-bubble-bg:#fff;--cursor-buddy-bubble-text:#1f2937;--cursor-buddy-bubble-radius:8px;--cursor-buddy-bubble-padding:8px 12px;--cursor-buddy-bubble-shadow:0 4px 12px #00000026;--cursor-buddy-bubble-max-width:200px;--cursor-buddy-bubble-font-size:14px;--cursor-buddy-waveform-color:#ef4444;--cursor-buddy-waveform-bar-width:4px;--cursor-buddy-waveform-bar-radius:2px;--cursor-buddy-waveform-gap:3px;--cursor-buddy-z-index:2147480000;--cursor-buddy-transition-fast:.1s;--cursor-buddy-transition-normal:.2s;--cursor-buddy-animation-duration:.3s}.cursor-buddy-overlay{pointer-events:none;isolation:isolate;z-index:var(--cursor-buddy-z-index);position:fixed;inset:0}.cursor-buddy-container{position:absolute;transform:translate(8px,4px)}.cursor-buddy-cursor{transition:transform var(--cursor-buddy-transition-fast) ease-out;filter:drop-shadow(var(--cursor-buddy-cursor-shadow))}.cursor-buddy-cursor polygon{stroke:var(--cursor-buddy-cursor-stroke);stroke-width:2px;transition:fill var(--cursor-buddy-transition-normal) ease-out}.cursor-buddy-cursor--idle polygon{fill:var(--cursor-buddy-color-idle)}.cursor-buddy-cursor--listening polygon{fill:var(--cursor-buddy-color-listening)}.cursor-buddy-cursor--processing polygon{fill:var(--cursor-buddy-color-processing)}.cursor-buddy-cursor--responding polygon{fill:var(--cursor-buddy-color-responding)}.cursor-buddy-cursor__spinner{color:var(--cursor-buddy-color-processing)}.cursor-buddy-cursor--listening{animation:1.5s ease-in-out infinite cursor-buddy-pulse}@keyframes cursor-buddy-pulse{0%,to{filter:drop-shadow(var(--cursor-buddy-cursor-shadow))}50%{filter:drop-shadow(0 0 8px var(--cursor-buddy-color-listening))}}.cursor-buddy-cursor--processing{animation:2s linear infinite cursor-buddy-spin-subtle}@keyframes cursor-buddy-spin-subtle{0%{filter:drop-shadow(var(--cursor-buddy-cursor-shadow)) hue-rotate(0deg)}to{filter:drop-shadow(var(--cursor-buddy-cursor-shadow)) hue-rotate(360deg)}}.cursor-buddy-bubble{pointer-events:auto;cursor:pointer;max-width:var(--cursor-buddy-bubble-max-width);padding:var(--cursor-buddy-bubble-padding);background-color:var(--cursor-buddy-bubble-bg);color:var(--cursor-buddy-bubble-text);border-radius:var(--cursor-buddy-bubble-radius);box-shadow:var(--cursor-buddy-bubble-shadow);font-size:var(--cursor-buddy-bubble-font-size);overflow-wrap:break-word;word-break:break-word;user-select:none;width:max-content;animation:cursor-buddy-fade-in var(--cursor-buddy-animation-duration) ease-out;line-height:1.4;position:absolute;top:-8px;left:24px}@keyframes cursor-buddy-fade-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.cursor-buddy-waveform{align-items:center;gap:var(--cursor-buddy-waveform-gap);height:24px;animation:cursor-buddy-fade-in var(--cursor-buddy-animation-duration) ease-out;display:flex;position:absolute;top:2px;left:24px}.cursor-buddy-waveform-bar{width:var(--cursor-buddy-waveform-bar-width);background-color:var(--cursor-buddy-waveform-color);border-radius:var(--cursor-buddy-waveform-bar-radius);transition:height 50ms ease-out}.cursor-buddy-fade-out{animation:cursor-buddy-fade-out var(--cursor-buddy-animation-duration) ease-out forwards}@keyframes cursor-buddy-fade-out{0%{opacity:1}to{opacity:0}}:root{--cursor-buddy-tool-bg:#fff;--cursor-buddy-tool-text:#1f2937;--cursor-buddy-tool-border:#e5e7eb;--cursor-buddy-tool-radius:8px;--cursor-buddy-tool-padding:8px 12px;--cursor-buddy-tool-shadow:0 4px 12px #00000026;--cursor-buddy-tool-gap:8px;--cursor-buddy-tool-pending:#3b82f6;--cursor-buddy-tool-approval:#f59e0b;--cursor-buddy-tool-success:#22c55e;--cursor-buddy-tool-error:#ef4444;--cursor-buddy-tool-denied:#6b7280;--cursor-buddy-tool-btn-approve-bg:#22c55e;--cursor-buddy-tool-btn-approve-text:#fff;--cursor-buddy-tool-btn-deny-bg:#ef4444;--cursor-buddy-tool-btn-deny-text:#fff}.cursor-buddy-tool-stack{gap:var(--cursor-buddy-tool-gap);pointer-events:auto;flex-direction:column;display:flex;position:absolute;top:32px;left:24px}.cursor-buddy-tool-stack__item{animation:cursor-buddy-fade-in var(--cursor-buddy-animation-duration) ease-out}.cursor-buddy-tool-bubble{padding:var(--cursor-buddy-tool-padding);background-color:var(--cursor-buddy-tool-bg);color:var(--cursor-buddy-tool-text);border-radius:var(--cursor-buddy-tool-radius);box-shadow:var(--cursor-buddy-tool-shadow);white-space:nowrap;user-select:none;border-left:3px solid var(--cursor-buddy-tool-pending);align-items:center;gap:8px;font-size:13px;line-height:1.4;display:flex}.cursor-buddy-tool-bubble--pending,.cursor-buddy-tool-bubble--approved{border-left-color:var(--cursor-buddy-tool-pending)}.cursor-buddy-tool-bubble--awaiting_approval{border-left-color:var(--cursor-buddy-tool-approval)}.cursor-buddy-tool-bubble--completed{border-left-color:var(--cursor-buddy-tool-success)}.cursor-buddy-tool-bubble--failed{border-left-color:var(--cursor-buddy-tool-error)}.cursor-buddy-tool-bubble--denied{border-left-color:var(--cursor-buddy-tool-denied)}.cursor-buddy-tool-bubble__content{align-items:center;gap:6px;display:flex}.cursor-buddy-tool-bubble__label{font-weight:500}.cursor-buddy-tool-icon{justify-content:center;align-items:center;width:16px;height:16px;font-size:12px;font-weight:700;display:inline-flex}.cursor-buddy-tool-icon--spinner{border:2px solid var(--cursor-buddy-tool-pending);border-top-color:#0000;border-radius:50%;width:14px;height:14px;animation:.8s linear infinite cursor-buddy-tool-spin}@keyframes cursor-buddy-tool-spin{to{transform:rotate(360deg)}}.cursor-buddy-tool-icon--check{color:var(--cursor-buddy-tool-success)}.cursor-buddy-tool-icon--error{color:var(--cursor-buddy-tool-error)}.cursor-buddy-tool-icon--denied{color:var(--cursor-buddy-tool-denied)}.cursor-buddy-tool-icon--question{color:var(--cursor-buddy-tool-approval)}.cursor-buddy-tool-bubble__actions{gap:4px;margin-left:4px;display:flex}.cursor-buddy-tool-button{cursor:pointer;border:none;border-radius:4px;padding:4px 10px;font-size:12px;font-weight:500;transition:opacity .15s}.cursor-buddy-tool-button:hover{opacity:.9}.cursor-buddy-tool-button:active{opacity:.8}.cursor-buddy-tool-button--approve{background-color:var(--cursor-buddy-tool-btn-approve-bg);color:var(--cursor-buddy-tool-btn-approve-text)}.cursor-buddy-tool-button--deny{background-color:var(--cursor-buddy-tool-btn-deny-bg);color:var(--cursor-buddy-tool-btn-deny-text)}.cursor-buddy-tool-bubble__dismiss{color:#9ca3af;cursor:pointer;background-color:#0000;border:none;border-radius:50%;justify-content:center;align-items:center;width:18px;height:18px;margin-left:auto;padding:0;font-size:12px;transition:color .15s,background-color .15s;display:flex}.cursor-buddy-tool-bubble__dismiss:hover{color:#6b7280;background-color:#f3f4f6}";
9
9
  //#endregion
10
10
  //#region src/react/utils/inject-styles.ts
11
11
  const STYLE_ID = "cursor-buddy-styles";
@@ -36,15 +36,18 @@ const CursorBuddyContext = createContext(null);
36
36
  /**
37
37
  * Provider for cursor buddy. Creates and manages the client instance.
38
38
  */
39
- function CursorBuddyProvider({ endpoint, transcription, speech, children, onTranscript, onResponse, onPoint, onStateChange, onError }) {
39
+ function CursorBuddyProvider({ endpoint, transcription, speech, toolDisplay, children, onTranscript, onResponse, onPoint, onStateChange, onError, onToolCall, onToolResult }) {
40
40
  const [client] = useState(() => new CursorBuddyClient(endpoint, {
41
41
  onTranscript,
42
42
  onResponse,
43
43
  onPoint,
44
44
  onStateChange,
45
45
  onError,
46
+ onToolCall,
47
+ onToolResult,
46
48
  speech,
47
- transcription
49
+ transcription,
50
+ toolDisplay
48
51
  }));
49
52
  useEffect(() => {
50
53
  injectStyles();
@@ -93,7 +96,10 @@ function useCursorBuddy() {
93
96
  setEnabled: useCallback((enabled) => client.setEnabled(enabled), [client]),
94
97
  pointAt: useCallback((x, y, label) => client.pointAt(x, y, label), [client]),
95
98
  dismissPointing: useCallback(() => client.dismissPointing(), [client]),
96
- reset: useCallback(() => client.reset(), [client])
99
+ reset: useCallback(() => client.reset(), [client]),
100
+ approveToolCall: useCallback((id) => client.approveToolCall(id), [client]),
101
+ denyToolCall: useCallback((id) => client.denyToolCall(id), [client]),
102
+ dismissToolCall: useCallback((id) => client.dismissToolCall(id), [client])
97
103
  };
98
104
  }
99
105
  //#endregion
@@ -300,6 +306,61 @@ function createHotkeyController(hotkey, options) {
300
306
  };
301
307
  }
302
308
  //#endregion
309
+ //#region src/core/hotkeys/approval-shortcuts.ts
310
+ /**
311
+ * Approval shortcut keys
312
+ */
313
+ const APPROVE_KEYS = ["y", "enter"];
314
+ const DENY_KEYS = ["n", "escape"];
315
+ /**
316
+ * Create a controller for approval keyboard shortcuts.
317
+ *
318
+ * Listens for:
319
+ * - Y or Enter → onApprove
320
+ * - N or Escape → onDeny
321
+ *
322
+ * @example
323
+ * ```ts
324
+ * const controller = createApprovalShortcutController({
325
+ * onApprove: () => approveToolCall(id),
326
+ * onDeny: () => denyToolCall(id),
327
+ * enabled: pendingApproval !== null,
328
+ * })
329
+ *
330
+ * // Later, cleanup
331
+ * controller.destroy()
332
+ * ```
333
+ */
334
+ function createApprovalShortcutController(options) {
335
+ let enabled = options.enabled ?? true;
336
+ const { onApprove, onDeny } = options;
337
+ function handleKeyDown(event) {
338
+ if (!enabled) return;
339
+ const target = event.target;
340
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) return;
341
+ const key = event.key.toLowerCase();
342
+ if (APPROVE_KEYS.includes(key)) {
343
+ event.preventDefault();
344
+ onApprove();
345
+ } else if (DENY_KEYS.includes(key)) {
346
+ event.preventDefault();
347
+ onDeny();
348
+ }
349
+ }
350
+ window.addEventListener("keydown", handleKeyDown);
351
+ return {
352
+ get isEnabled() {
353
+ return enabled;
354
+ },
355
+ setEnabled(newEnabled) {
356
+ enabled = newEnabled;
357
+ },
358
+ destroy() {
359
+ window.removeEventListener("keydown", handleKeyDown);
360
+ }
361
+ };
362
+ }
363
+ //#endregion
303
364
  //#region src/react/use-hotkey.ts
304
365
  /**
305
366
  * Hook for detecting hotkey press/release.
@@ -352,6 +413,52 @@ function useHotkey(hotkey, onPress, onRelease, enabled = true) {
352
413
  }, [enabled]);
353
414
  }
354
415
  //#endregion
416
+ //#region src/react/use-approval-shortcuts.ts
417
+ /**
418
+ * Hook for approval keyboard shortcuts.
419
+ *
420
+ * When enabled, listens for:
421
+ * - Y or Enter → onApprove
422
+ * - N or Escape → onDeny
423
+ *
424
+ * Automatically enables when `enabled` is true.
425
+ * Ignores keypresses when focus is in an input/textarea.
426
+ *
427
+ * @param enabled - Whether shortcuts should be active
428
+ * @param onApprove - Called when user presses Y or Enter
429
+ * @param onDeny - Called when user presses N or Escape
430
+ *
431
+ * @example
432
+ * ```tsx
433
+ * useApprovalShortcuts(
434
+ * pendingApproval !== null,
435
+ * () => approveToolCall(pendingApproval.id),
436
+ * () => denyToolCall(pendingApproval.id)
437
+ * )
438
+ * ```
439
+ */
440
+ function useApprovalShortcuts(enabled, onApprove, onDeny) {
441
+ const onApproveRef = useRef(onApprove);
442
+ const onDenyRef = useRef(onDeny);
443
+ onApproveRef.current = onApprove;
444
+ onDenyRef.current = onDeny;
445
+ const controllerRef = useRef(null);
446
+ useEffect(() => {
447
+ controllerRef.current = createApprovalShortcutController({
448
+ onApprove: () => onApproveRef.current(),
449
+ onDeny: () => onDenyRef.current(),
450
+ enabled
451
+ });
452
+ return () => {
453
+ controllerRef.current?.destroy();
454
+ controllerRef.current = null;
455
+ };
456
+ }, []);
457
+ useEffect(() => {
458
+ controllerRef.current?.setEnabled(enabled);
459
+ }, [enabled]);
460
+ }
461
+ //#endregion
355
462
  //#region src/react/components/Cursor.tsx
356
463
  const BASE_ROTATION = -Math.PI / 6;
357
464
  /**
@@ -425,6 +532,138 @@ function DefaultSpeechBubble({ text, isVisible, onClick }) {
425
532
  });
426
533
  }
427
534
  //#endregion
535
+ //#region src/react/components/ToolBubble.tsx
536
+ /**
537
+ * Status icons for tool call states
538
+ */
539
+ function StatusIcon({ status }) {
540
+ switch (status) {
541
+ case "pending":
542
+ case "approved": return /* @__PURE__ */ jsx("span", {
543
+ className: "cursor-buddy-tool-icon cursor-buddy-tool-icon--spinner",
544
+ "aria-label": "Loading"
545
+ });
546
+ case "awaiting_approval": return /* @__PURE__ */ jsx("span", {
547
+ className: "cursor-buddy-tool-icon cursor-buddy-tool-icon--question",
548
+ "aria-label": "Needs approval",
549
+ children: "?"
550
+ });
551
+ case "completed": return /* @__PURE__ */ jsx("span", {
552
+ className: "cursor-buddy-tool-icon cursor-buddy-tool-icon--check",
553
+ "aria-label": "Completed",
554
+ children: "✓"
555
+ });
556
+ case "denied": return /* @__PURE__ */ jsx("span", {
557
+ className: "cursor-buddy-tool-icon cursor-buddy-tool-icon--denied",
558
+ "aria-label": "Denied",
559
+ children: "✕"
560
+ });
561
+ case "failed": return /* @__PURE__ */ jsx("span", {
562
+ className: "cursor-buddy-tool-icon cursor-buddy-tool-icon--error",
563
+ "aria-label": "Failed",
564
+ children: "!"
565
+ });
566
+ default: return null;
567
+ }
568
+ }
569
+ /**
570
+ * Default tool bubble component.
571
+ * Displays tool call status with optional approve/deny buttons.
572
+ */
573
+ function ToolBubble({ toolName, args, status, label, result, error, approve, deny, dismiss, customRender }) {
574
+ if (customRender) return /* @__PURE__ */ jsx(Fragment, { children: customRender({
575
+ toolName,
576
+ args,
577
+ status,
578
+ label,
579
+ result,
580
+ error,
581
+ approve,
582
+ deny,
583
+ dismiss
584
+ }) });
585
+ const needsApproval = status === "awaiting_approval";
586
+ const isTerminal = status === "completed" || status === "denied" || status === "failed";
587
+ return /* @__PURE__ */ jsxs("div", {
588
+ className: `cursor-buddy-tool-bubble cursor-buddy-tool-bubble--${status}`,
589
+ role: "status",
590
+ "aria-live": "polite",
591
+ children: [
592
+ /* @__PURE__ */ jsxs("div", {
593
+ className: "cursor-buddy-tool-bubble__content",
594
+ children: [/* @__PURE__ */ jsx(StatusIcon, { status }), /* @__PURE__ */ jsx("span", {
595
+ className: "cursor-buddy-tool-bubble__label",
596
+ children: label
597
+ })]
598
+ }),
599
+ needsApproval && approve && deny && /* @__PURE__ */ jsxs("div", {
600
+ className: "cursor-buddy-tool-bubble__actions",
601
+ children: [/* @__PURE__ */ jsx("button", {
602
+ type: "button",
603
+ className: "cursor-buddy-tool-button cursor-buddy-tool-button--approve",
604
+ onClick: approve,
605
+ "aria-label": `Approve ${toolName}`,
606
+ children: "Yes (Y)"
607
+ }), /* @__PURE__ */ jsx("button", {
608
+ type: "button",
609
+ className: "cursor-buddy-tool-button cursor-buddy-tool-button--deny",
610
+ onClick: deny,
611
+ "aria-label": `Deny ${toolName}`,
612
+ children: "No (Esc)"
613
+ })]
614
+ }),
615
+ isTerminal && /* @__PURE__ */ jsx("button", {
616
+ type: "button",
617
+ className: "cursor-buddy-tool-bubble__dismiss",
618
+ onClick: dismiss,
619
+ "aria-label": "Dismiss",
620
+ children: "✕"
621
+ })
622
+ ]
623
+ });
624
+ }
625
+ //#endregion
626
+ //#region src/react/components/ToolBubbleStack.tsx
627
+ /**
628
+ * Stack of tool bubbles displayed near the cursor.
629
+ * Renders all active tool calls in a vertical column.
630
+ */
631
+ function ToolBubbleStack({ toolCalls, toolDisplay, onApprove, onDeny, onDismiss, renderToolBubble }) {
632
+ if (toolCalls.length === 0) return null;
633
+ return /* @__PURE__ */ jsx("div", {
634
+ className: "cursor-buddy-tool-stack",
635
+ role: "region",
636
+ "aria-label": "Tool calls",
637
+ children: toolCalls.map((toolCall) => {
638
+ const toolConfig = toolDisplay?.[toolCall.toolName] ?? toolDisplay?.["*"];
639
+ if (toolConfig?.mode === "hidden") return null;
640
+ const needsApproval = toolCall.status === "awaiting_approval";
641
+ const bubbleProps = {
642
+ toolName: toolCall.toolName,
643
+ args: toolCall.args,
644
+ status: toolCall.status,
645
+ label: toolCall.label,
646
+ result: toolCall.result,
647
+ error: toolCall.error,
648
+ approve: needsApproval ? () => onApprove(toolCall.id) : void 0,
649
+ deny: needsApproval ? () => onDeny(toolCall.id) : void 0,
650
+ dismiss: () => onDismiss(toolCall.id)
651
+ };
652
+ if (renderToolBubble) return /* @__PURE__ */ jsx("div", {
653
+ className: "cursor-buddy-tool-stack__item",
654
+ children: renderToolBubble(bubbleProps)
655
+ }, toolCall.id);
656
+ return /* @__PURE__ */ jsx("div", {
657
+ className: "cursor-buddy-tool-stack__item",
658
+ children: /* @__PURE__ */ jsx(ToolBubble, {
659
+ ...bubbleProps,
660
+ customRender: toolConfig?.render
661
+ })
662
+ }, toolCall.id);
663
+ })
664
+ });
665
+ }
666
+ //#endregion
428
667
  //#region src/react/components/Waveform.tsx
429
668
  const EMPTY_BARS = Array.from({ length: 12 }, () => 0);
430
669
  /**
@@ -460,13 +699,14 @@ function DefaultWaveform({ audioLevel, isListening }) {
460
699
  //#endregion
461
700
  //#region src/react/components/Overlay.tsx
462
701
  /**
463
- * Overlay component that renders the cursor, speech bubble, and waveform.
702
+ * Overlay component that renders the cursor, speech bubble, waveform, and tool bubbles.
464
703
  * Uses React portal to render at the document body level.
465
704
  */
466
- function Overlay({ cursor, speechBubble, waveform, container }) {
705
+ function Overlay({ cursor, speechBubble, waveform, toolDisplay, renderToolBubble, container }) {
467
706
  const [isMounted, setIsMounted] = useState(false);
468
707
  useEffect(() => setIsMounted(true), []);
469
- const { state, isPointing, isEnabled, dismissPointing } = useCursorBuddy();
708
+ const { state, isPointing, isEnabled, dismissPointing, activeToolCalls, pendingApproval, approveToolCall, denyToolCall, dismissToolCall } = useCursorBuddy();
709
+ useApprovalShortcuts(pendingApproval !== null, () => pendingApproval && approveToolCall(pendingApproval.id), () => pendingApproval && denyToolCall(pendingApproval.id));
470
710
  const buddyPosition = useStore($buddyPosition);
471
711
  const buddyRotation = useStore($buddyRotation);
472
712
  const buddyScale = useStore($buddyScale);
@@ -503,7 +743,15 @@ function Overlay({ cursor, speechBubble, waveform, container }) {
503
743
  children: [
504
744
  cursorElement,
505
745
  state === "listening" && waveformElement,
506
- isPointing && speechBubbleElement
746
+ isPointing && speechBubbleElement,
747
+ /* @__PURE__ */ jsx(ToolBubbleStack, {
748
+ toolCalls: activeToolCalls,
749
+ toolDisplay,
750
+ onApprove: approveToolCall,
751
+ onDeny: denyToolCall,
752
+ onDismiss: dismissToolCall,
753
+ renderToolBubble
754
+ })
507
755
  ]
508
756
  })
509
757
  });
@@ -516,13 +764,15 @@ function Overlay({ cursor, speechBubble, waveform, container }) {
516
764
  /**
517
765
  * Internal component that sets up hotkey handling
518
766
  */
519
- function CursorBuddyInner({ hotkey = "ctrl+alt", cursor, speechBubble, waveform, container }) {
767
+ function CursorBuddyInner({ hotkey = "ctrl+alt", cursor, speechBubble, waveform, toolDisplay, renderToolBubble, container }) {
520
768
  const { startListening, stopListening, isEnabled } = useCursorBuddy();
521
769
  useHotkey(hotkey, startListening, stopListening, isEnabled);
522
770
  return /* @__PURE__ */ jsx(Overlay, {
523
771
  cursor,
524
772
  speechBubble,
525
773
  waveform,
774
+ toolDisplay,
775
+ renderToolBubble,
526
776
  container
527
777
  });
528
778
  }
@@ -549,26 +799,31 @@ function CursorBuddyInner({ hotkey = "ctrl+alt", cursor, speechBubble, waveform,
549
799
  * }
550
800
  * ```
551
801
  */
552
- function CursorBuddy({ endpoint, hotkey, container, speech, transcription, cursor, speechBubble, waveform, onTranscript, onResponse, onPoint, onStateChange, onError }) {
802
+ function CursorBuddy({ endpoint, hotkey, container, speech, transcription, cursor, speechBubble, waveform, toolDisplay, renderToolBubble, onTranscript, onResponse, onPoint, onStateChange, onError, onToolCall, onToolResult }) {
553
803
  return /* @__PURE__ */ jsx(CursorBuddyProvider, {
554
804
  endpoint,
555
805
  speech,
556
806
  transcription,
807
+ toolDisplay,
557
808
  onTranscript,
558
809
  onResponse,
559
810
  onPoint,
560
811
  onStateChange,
561
812
  onError,
813
+ onToolCall,
814
+ onToolResult,
562
815
  children: /* @__PURE__ */ jsx(CursorBuddyInner, {
563
816
  hotkey,
564
817
  cursor,
565
818
  speechBubble,
566
819
  waveform,
820
+ toolDisplay,
821
+ renderToolBubble,
567
822
  container
568
823
  })
569
824
  });
570
825
  }
571
826
  //#endregion
572
- export { CursorBuddy, CursorBuddyProvider, useCursorBuddy };
827
+ export { CursorBuddy, CursorBuddyProvider, ToolBubble, ToolBubbleStack, useCursorBuddy };
573
828
 
574
829
  //# sourceMappingURL=index.mjs.map