@appilots/web-sdk 0.2.0 → 0.3.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.
- package/README.md +19 -0
- package/dist/{chunk-T5TUFZ6T.js → chunk-H4ITA2QP.js} +255 -16
- package/dist/{chunk-VHRE4RUO.mjs → chunk-RR3T5CPP.mjs} +250 -17
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +70 -58
- package/dist/index.mjs +1 -1
- package/dist/react/index.d.mts +19 -5
- package/dist/react/index.d.ts +19 -5
- package/dist/react/index.js +516 -101
- package/dist/react/index.mjs +505 -98
- package/dist/{AppilotsWebRuntime-DZF26UyU.d.mts → webSpeechHost-CrTKADeb.d.mts} +127 -1
- package/dist/{AppilotsWebRuntime-DZF26UyU.d.ts → webSpeechHost-CrTKADeb.d.ts} +127 -1
- package/package.json +1 -1
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkH4ITA2QP_js = require('../chunk-H4ITA2QP.js');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
var reactDom = require('react-dom');
|
|
@@ -9,7 +9,7 @@ var RuntimeContext = react.createContext(null);
|
|
|
9
9
|
function AppilotsWebProvider(props) {
|
|
10
10
|
const { children, runtime: injected, ...options } = props;
|
|
11
11
|
const runtime = react.useMemo(
|
|
12
|
-
() => injected ?? new
|
|
12
|
+
() => injected ?? new chunkH4ITA2QP_js.AppilotsWebRuntime(options),
|
|
13
13
|
// Session credentials and host identity define the runtime lifetime.
|
|
14
14
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
15
15
|
[
|
|
@@ -69,6 +69,9 @@ function useAppilotsChat(options = {}) {
|
|
|
69
69
|
const cancelMessage = react.useCallback(() => chat.cancelMessage(), [chat]);
|
|
70
70
|
const clearMessages = react.useCallback(() => chat.clearMessages(), [chat]);
|
|
71
71
|
const clearError = react.useCallback(() => chat.clearError(), [chat]);
|
|
72
|
+
const pauseMission = react.useCallback(() => chat.pauseMission(), [chat]);
|
|
73
|
+
const resumeMission = react.useCallback(() => chat.resumeMission(), [chat]);
|
|
74
|
+
const cancelMission = react.useCallback(() => chat.cancelMission(), [chat]);
|
|
72
75
|
return {
|
|
73
76
|
messages: state.messages,
|
|
74
77
|
isLoading: state.isLoading,
|
|
@@ -76,6 +79,10 @@ function useAppilotsChat(options = {}) {
|
|
|
76
79
|
error: state.error,
|
|
77
80
|
pendingActions: state.pendingActions,
|
|
78
81
|
escalation: state.escalation,
|
|
82
|
+
mission: state.mission ?? null,
|
|
83
|
+
pauseMission,
|
|
84
|
+
resumeMission,
|
|
85
|
+
cancelMission,
|
|
79
86
|
requestHuman,
|
|
80
87
|
sendMessage,
|
|
81
88
|
cancelMessage,
|
|
@@ -102,6 +109,78 @@ function useAppilotsActions() {
|
|
|
102
109
|
rejectAction
|
|
103
110
|
};
|
|
104
111
|
}
|
|
112
|
+
function useActionStatus(locale) {
|
|
113
|
+
const { actions } = useAppilotsRuntime();
|
|
114
|
+
const state = react.useSyncExternalStore(actions.subscribeState, actions.getState, actions.getState);
|
|
115
|
+
const current = state.actions.find((a) => a.status === "executing" && a.type !== "confirm");
|
|
116
|
+
return current ? chunkH4ITA2QP_js.describeAction(current, locale).label : "";
|
|
117
|
+
}
|
|
118
|
+
function useVoiceInput({ host, locale, onText }) {
|
|
119
|
+
const [state, setState] = react.useState("idle");
|
|
120
|
+
const run = react.useRef(0);
|
|
121
|
+
const onTextRef = react.useRef(onText);
|
|
122
|
+
onTextRef.current = onText;
|
|
123
|
+
const stateRef = react.useRef(state);
|
|
124
|
+
stateRef.current = state;
|
|
125
|
+
const cancel = react.useCallback(() => {
|
|
126
|
+
if (stateRef.current === "idle") return;
|
|
127
|
+
run.current++;
|
|
128
|
+
setState("idle");
|
|
129
|
+
void host?.cancel().catch((err) => chunkH4ITA2QP_js.appilotsDebugWarn("useVoiceInput: cancel failed", err));
|
|
130
|
+
}, [host]);
|
|
131
|
+
const toggle = react.useCallback(() => {
|
|
132
|
+
if (!host) return;
|
|
133
|
+
const id = ++run.current;
|
|
134
|
+
const current = stateRef.current;
|
|
135
|
+
if (current === "idle" || current === "error") {
|
|
136
|
+
setState("starting");
|
|
137
|
+
host.start({ locale }).then(
|
|
138
|
+
() => id === run.current && setState("listening"),
|
|
139
|
+
(err) => {
|
|
140
|
+
chunkH4ITA2QP_js.appilotsDebugWarn("useVoiceInput: start failed", err);
|
|
141
|
+
if (id === run.current) setState("error");
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
} else if (current === "starting" || current === "listening") {
|
|
145
|
+
setState("stopping");
|
|
146
|
+
host.stop().then(
|
|
147
|
+
(text) => {
|
|
148
|
+
if (id !== run.current) return;
|
|
149
|
+
onTextRef.current(text ?? "");
|
|
150
|
+
setState("idle");
|
|
151
|
+
},
|
|
152
|
+
(err) => {
|
|
153
|
+
chunkH4ITA2QP_js.appilotsDebugWarn("useVoiceInput: stop failed", err);
|
|
154
|
+
if (id === run.current) setState("error");
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}, [host, locale]);
|
|
159
|
+
const active = state === "starting" || state === "listening";
|
|
160
|
+
react.useEffect(() => {
|
|
161
|
+
if (!host || !active) return;
|
|
162
|
+
const id = run.current;
|
|
163
|
+
const offPartial = host.onPartial?.((text) => id === run.current && onTextRef.current(text));
|
|
164
|
+
const offEnd = host.onEnd?.((error) => {
|
|
165
|
+
if (id !== run.current) return;
|
|
166
|
+
run.current++;
|
|
167
|
+
setState(error ? "error" : "idle");
|
|
168
|
+
});
|
|
169
|
+
return () => {
|
|
170
|
+
offPartial?.();
|
|
171
|
+
offEnd?.();
|
|
172
|
+
};
|
|
173
|
+
}, [host, active]);
|
|
174
|
+
react.useEffect(
|
|
175
|
+
() => () => {
|
|
176
|
+
const s = stateRef.current;
|
|
177
|
+
if (s === "starting" || s === "listening") void host?.cancel().catch(() => {
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
[host]
|
|
181
|
+
);
|
|
182
|
+
return { state, toggle, cancel };
|
|
183
|
+
}
|
|
105
184
|
|
|
106
185
|
// src/react/confirmCard.ts
|
|
107
186
|
var GENERIC_MESSAGE = "Confirm this action?";
|
|
@@ -117,14 +196,19 @@ function deriveConfirmCardCopy(payload, defaults = {
|
|
|
117
196
|
};
|
|
118
197
|
const actionDescription = str("actionDescription");
|
|
119
198
|
const message = str("message") ?? str("title") ?? (actionDescription ? defaults.describe(actionDescription) : void 0) ?? defaults.message;
|
|
199
|
+
const confirmLabel = str("confirmLabel") ?? defaults.confirmLabel;
|
|
200
|
+
const cancelLabel = str("cancelLabel") ?? defaults.cancelLabel;
|
|
201
|
+
const name = (label) => actionDescription ? `${label}: ${actionDescription}` : label;
|
|
120
202
|
return {
|
|
121
203
|
message,
|
|
122
|
-
confirmLabel
|
|
123
|
-
cancelLabel
|
|
204
|
+
confirmLabel,
|
|
205
|
+
cancelLabel,
|
|
206
|
+
confirmName: name(confirmLabel),
|
|
207
|
+
cancelName: name(cancelLabel)
|
|
124
208
|
};
|
|
125
209
|
}
|
|
126
|
-
function inline(text) {
|
|
127
|
-
return
|
|
210
|
+
function inline(text, newTab) {
|
|
211
|
+
return chunkH4ITA2QP_js.parseInlineMarkdown(text).map((segment, index) => {
|
|
128
212
|
switch (segment.kind) {
|
|
129
213
|
case "bold":
|
|
130
214
|
return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: segment.value }, index);
|
|
@@ -133,28 +217,38 @@ function inline(text) {
|
|
|
133
217
|
case "code":
|
|
134
218
|
return /* @__PURE__ */ jsxRuntime.jsx("code", { children: segment.value }, index);
|
|
135
219
|
case "link":
|
|
136
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
220
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("a", { href: segment.href, target: "_blank", rel: "noopener noreferrer", children: [
|
|
221
|
+
segment.value,
|
|
222
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ap-sr-only", children: [
|
|
223
|
+
" ",
|
|
224
|
+
newTab
|
|
225
|
+
] })
|
|
226
|
+
] }, index);
|
|
137
227
|
default:
|
|
138
228
|
return segment.value;
|
|
139
229
|
}
|
|
140
230
|
});
|
|
141
231
|
}
|
|
142
|
-
function ChatMarkdown({
|
|
143
|
-
|
|
232
|
+
function ChatMarkdown({
|
|
233
|
+
content,
|
|
234
|
+
hiddenValue,
|
|
235
|
+
newTab
|
|
236
|
+
}) {
|
|
237
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-markdown", children: chunkH4ITA2QP_js.parseMarkdownBlocks(content.replaceAll("<redacted>", hiddenValue)).map((block, index) => {
|
|
144
238
|
switch (block.kind) {
|
|
145
239
|
case "heading": {
|
|
146
|
-
const Heading = `h${block.level}`;
|
|
147
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Heading, { children: inline(block.text) }, index);
|
|
240
|
+
const Heading = `h${block.level + 2}`;
|
|
241
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Heading, { children: inline(block.text, newTab) }, index);
|
|
148
242
|
}
|
|
149
243
|
case "codeblock":
|
|
150
244
|
return /* @__PURE__ */ jsxRuntime.jsx("pre", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: block.code }) }, index);
|
|
151
245
|
case "bullet":
|
|
152
246
|
case "ordered": {
|
|
153
247
|
const List = block.kind === "bullet" ? "ul" : "ol";
|
|
154
|
-
return /* @__PURE__ */ jsxRuntime.jsx(List, { children: block.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: inline(item) }, i)) }, index);
|
|
248
|
+
return /* @__PURE__ */ jsxRuntime.jsx(List, { children: block.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: inline(item, newTab) }, i)) }, index);
|
|
155
249
|
}
|
|
156
250
|
default:
|
|
157
|
-
return /* @__PURE__ */ jsxRuntime.jsx("p", { children: inline(block.text) }, index);
|
|
251
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { children: inline(block.text, newTab) }, index);
|
|
158
252
|
}
|
|
159
253
|
}) });
|
|
160
254
|
}
|
|
@@ -183,17 +277,31 @@ var chatStrings = {
|
|
|
183
277
|
stop: "Stop response",
|
|
184
278
|
inputHint: "Enter to send \xB7 Shift + Enter for a new line",
|
|
185
279
|
latest: "Latest messages",
|
|
280
|
+
messages: "Messages",
|
|
281
|
+
you: "You",
|
|
282
|
+
newTab: "(opens in a new tab)",
|
|
186
283
|
thinking: "Thinking\u2026",
|
|
187
284
|
statusAnalyzing: "Analyzing\u2026",
|
|
188
285
|
statusWaitingApp: "Waiting for the app\u2026",
|
|
189
286
|
statusAdjusting: "Adjusting\u2026",
|
|
287
|
+
stillWorking: (seconds) => `Still working\u2026 ${seconds} s`,
|
|
190
288
|
errorPrefix: "Something went wrong",
|
|
191
289
|
dismiss: "Dismiss error",
|
|
192
290
|
approval: "Your approval is needed",
|
|
193
291
|
confirm: "Confirm",
|
|
194
292
|
cancel: "Cancel",
|
|
195
293
|
confirmMessage: "Confirm this action?",
|
|
196
|
-
confirmDescription: (label) => `Confirm "${label}"
|
|
294
|
+
confirmDescription: (label) => `Confirm "${label}"?`,
|
|
295
|
+
confirmExpired: "This confirmation expired. Ask the assistant again to continue.",
|
|
296
|
+
missionControls: "Task controls",
|
|
297
|
+
missionPause: "Pause",
|
|
298
|
+
missionResume: "Resume task",
|
|
299
|
+
missionCancel: "Cancel task",
|
|
300
|
+
missionPaused: "Paused",
|
|
301
|
+
voice: "Dictate by voice",
|
|
302
|
+
voiceListening: "Listening. Press the microphone again to stop.",
|
|
303
|
+
voiceStopped: "Stopped listening. Review the text before sending.",
|
|
304
|
+
voiceError: "Could not use the microphone."
|
|
197
305
|
},
|
|
198
306
|
"pt-BR": {
|
|
199
307
|
openChat: "Abrir chat",
|
|
@@ -217,88 +325,118 @@ var chatStrings = {
|
|
|
217
325
|
stop: "Parar resposta",
|
|
218
326
|
inputHint: "Enter para enviar \xB7 Shift + Enter para nova linha",
|
|
219
327
|
latest: "Mensagens recentes",
|
|
328
|
+
messages: "Mensagens",
|
|
329
|
+
you: "Voc\xEA",
|
|
330
|
+
newTab: "(abre em nova aba)",
|
|
220
331
|
thinking: "Pensando\u2026",
|
|
221
332
|
statusAnalyzing: "Analisando\u2026",
|
|
222
333
|
statusWaitingApp: "Aguardando o aplicativo\u2026",
|
|
223
334
|
statusAdjusting: "Ajustando\u2026",
|
|
335
|
+
stillWorking: (seconds) => `Ainda trabalhando\u2026 ${seconds} s`,
|
|
224
336
|
errorPrefix: "Algo deu errado",
|
|
225
337
|
dismiss: "Fechar erro",
|
|
226
338
|
approval: "Precisamos da sua confirma\xE7\xE3o",
|
|
227
339
|
confirm: "Confirmar",
|
|
228
340
|
cancel: "Cancelar",
|
|
229
341
|
confirmMessage: "Confirma esta a\xE7\xE3o?",
|
|
230
|
-
confirmDescription: (label) => `Confirma "${label}"
|
|
342
|
+
confirmDescription: (label) => `Confirma "${label}"?`,
|
|
343
|
+
confirmExpired: "Esta confirma\xE7\xE3o expirou. Pe\xE7a de novo ao assistente para continuar.",
|
|
344
|
+
missionControls: "Controles da tarefa",
|
|
345
|
+
missionPause: "Pausar",
|
|
346
|
+
missionResume: "Retomar tarefa",
|
|
347
|
+
missionCancel: "Cancelar tarefa",
|
|
348
|
+
missionPaused: "Pausada",
|
|
349
|
+
voice: "Ditar por voz",
|
|
350
|
+
voiceListening: "Ouvindo. Pressione o microfone de novo para parar.",
|
|
351
|
+
voiceStopped: "Parou de ouvir. Revise o texto antes de enviar.",
|
|
352
|
+
voiceError: "N\xE3o foi poss\xEDvel usar o microfone."
|
|
231
353
|
}
|
|
232
354
|
};
|
|
233
355
|
|
|
234
356
|
// src/react/chatStyles.ts
|
|
235
357
|
var chatStyles = `
|
|
236
|
-
.appilots-chat { --ap-accent: #2563eb; display: flex; flex-direction: column; height: 100%; min-height:
|
|
358
|
+
.appilots-chat { --ap-accent: #2563eb; display: flex; flex-direction: column; height: 100%; min-height: 17.5rem; min-width: 0; color: #172033; background: #f6f8fb; border: 1px solid #e2e7ef; border-radius: 20px; overflow: hidden auto; font: 0.875rem/1.5 system-ui, -apple-system, sans-serif; box-shadow: 0 8px 32px #17203308; }
|
|
359
|
+
/* At large text sizes the header and footer outgrow the panel: they keep their size and the panel scrolls, instead of shrinking under their own text. */
|
|
360
|
+
.appilots-chat > * { flex-shrink: 0; }
|
|
237
361
|
.appilots-chat *, .appilots-chat *::before, .appilots-chat *::after { box-sizing: border-box; }
|
|
238
|
-
.appilots-chat button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; margin: 0; padding: 8px 12px; border: 1px solid #dde3ed; border-radius: 10px; background: #fff; color: inherit; cursor: pointer; font: inherit; line-height: 1.3; }
|
|
362
|
+
.appilots-chat button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 44px; margin: 0; padding: 8px 12px; border: 1px solid #dde3ed; border-radius: 10px; background: #fff; color: inherit; cursor: pointer; font: inherit; line-height: 1.3; }
|
|
239
363
|
.appilots-chat button:hover:not(:disabled) { background: #edf2f9; }
|
|
240
364
|
.appilots-chat button:disabled { opacity: .5; cursor: not-allowed; }
|
|
241
365
|
.appilots-chat button:focus-visible, .appilots-chat a:focus-visible, .appilots-chat textarea:focus-visible { outline: 2px solid var(--ap-accent); outline-offset: 3px; }
|
|
242
|
-
.appilots-chat .ap-header { display: flex; align-items: center; gap: 10px; padding: 18px; background: #fff; border-bottom: 1px solid #e8ecf2; }
|
|
366
|
+
.appilots-chat .ap-header { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 18px; background: #fff; border-bottom: 1px solid #e8ecf2; }
|
|
243
367
|
.appilots-chat .ap-avatar { display: grid; place-items: center; flex-shrink: 0; width: 38px; height: 38px; border-radius: 13px; background: #eaf0ff; color: var(--ap-accent); }
|
|
244
|
-
.appilots-chat .ap-identity { flex: 1; min-width: 0; }
|
|
245
|
-
.appilots-chat .ap-title { display: block; font-size:
|
|
246
|
-
.appilots-chat .ap-caption { font-size:
|
|
247
|
-
.appilots-chat .ap-icon { padding: 8px; width:
|
|
248
|
-
.appilots-chat .ap-human-button { border: 0; border-radius: 0; border-bottom: 1px solid #e8ecf2; padding: 10px 16px; color: #52647e; font-size:
|
|
249
|
-
.appilots-chat .ap-escalation { padding: 12px 18px; color: #16604b; background: #eaf7f1; font-size:
|
|
250
|
-
.appilots-chat .ap-log { flex: 1; min-height:
|
|
368
|
+
.appilots-chat .ap-identity { flex: 1 1 5rem; min-width: 0; }
|
|
369
|
+
.appilots-chat .ap-title { display: block; margin: 0; font-size: 0.9375rem; font-weight: 700; line-height: 1.4; overflow-wrap: anywhere; }
|
|
370
|
+
.appilots-chat .ap-caption { font-size: 0.75rem; color: #5f6b7d; }
|
|
371
|
+
.appilots-chat .ap-icon { padding: 8px; min-width: 44px; min-height: 44px; border: 0; background: transparent; flex-shrink: 0; }
|
|
372
|
+
.appilots-chat .ap-human-button { border: 0; border-radius: 0; border-bottom: 1px solid #e8ecf2; padding: 10px 16px; color: #52647e; font-size: 0.75rem; }
|
|
373
|
+
.appilots-chat .ap-escalation { padding: 12px 18px; color: #16604b; background: #eaf7f1; font-size: 0.75rem; border-bottom: 1px solid #d5ede2; }
|
|
374
|
+
.appilots-chat .ap-log { flex: 1; min-height: 6rem; overflow-y: auto; overscroll-behavior: contain; padding: 22px 18px; display: flex; flex-direction: column; gap: 20px; scrollbar-width: thin; }
|
|
251
375
|
.appilots-chat .ap-empty { margin: auto 0; padding: 24px 4px; text-align: center; }
|
|
252
376
|
.appilots-chat .ap-empty .ap-avatar { margin: 0 auto 18px; width: 52px; height: 52px; border-radius: 18px; }
|
|
253
|
-
.appilots-chat .ap-empty
|
|
254
|
-
.appilots-chat .ap-empty p { margin: 0 auto 24px; max-width:
|
|
377
|
+
.appilots-chat .ap-empty h3 { margin: 0 0 8px; font-size: 1.3125rem; font-weight: 650; letter-spacing: -.4px; }
|
|
378
|
+
.appilots-chat .ap-empty p { margin: 0 auto 24px; max-width: 16.25rem; color: #5f6b7d; }
|
|
255
379
|
.appilots-chat .ap-suggestions { display: flex; flex-direction: column; align-items: stretch; gap: 8px; }
|
|
256
|
-
.appilots-chat .ap-suggestions button { padding: 12px 14px; text-align: left; justify-content: space-between; font-size:
|
|
380
|
+
.appilots-chat .ap-suggestions button { padding: 12px 14px; text-align: left; justify-content: space-between; font-size: 0.8125rem; }
|
|
257
381
|
.appilots-chat .ap-message { max-width: 94%; align-self: flex-start; min-width: 0; }
|
|
258
382
|
.appilots-chat .ap-message-user { align-self: flex-end; max-width: 86%; }
|
|
259
|
-
.appilots-chat .ap-sender { display: block; margin: 0 0 5px 3px; color: #
|
|
383
|
+
.appilots-chat .ap-sender { display: block; margin: 0 0 5px 3px; color: #5f6b7d; font-size: 0.6875rem; font-weight: 600; }
|
|
260
384
|
.appilots-chat .ap-bubble { padding: 12px 15px; background: #fff; border: 1px solid #e7ebf2; border-radius: 4px 17px 17px; overflow-wrap: anywhere; white-space: pre-wrap; }
|
|
261
385
|
.appilots-chat .ap-message-user .ap-bubble { background: var(--ap-accent); color: #fff; border-color: transparent; border-radius: 17px 4px 17px 17px; }
|
|
262
386
|
.appilots-chat .ap-message-human_agent .ap-bubble { border-color: #cde7dc; }
|
|
263
|
-
.appilots-chat .ap-message time { display: block; margin: 5px 3px 0; color: #
|
|
387
|
+
.appilots-chat .ap-message time { display: block; margin: 5px 3px 0; color: #5f6b7d; font-size: 0.625rem; }
|
|
264
388
|
.appilots-chat .ap-message-user time { text-align: right; }
|
|
265
|
-
.appilots-chat .ap-system { align-self: center; text-align: center; color: #
|
|
389
|
+
.appilots-chat .ap-system { align-self: center; text-align: center; color: #5f6b7d; font-size: 0.75rem; white-space: pre-wrap; }
|
|
266
390
|
.appilots-chat .ap-system button { display: flex; margin: 8px auto 0; }
|
|
267
391
|
.appilots-chat .ap-markdown { white-space: normal; }
|
|
268
392
|
.appilots-chat .ap-markdown p { margin: 0 0 10px; white-space: pre-wrap; }
|
|
269
|
-
.appilots-chat .ap-markdown
|
|
393
|
+
.appilots-chat .ap-markdown h3, .appilots-chat .ap-markdown h4, .appilots-chat .ap-markdown h5 { font-size: 1rem; line-height: 1.4; margin: 12px 0 8px; }
|
|
270
394
|
.appilots-chat .ap-markdown ul, .appilots-chat .ap-markdown ol { margin: 8px 0; padding-left: 22px; }
|
|
271
395
|
.appilots-chat .ap-markdown li { margin: 4px 0; white-space: pre-wrap; }
|
|
272
|
-
.appilots-chat .ap-markdown code { font:
|
|
396
|
+
.appilots-chat .ap-markdown code { font: 0.75rem/1.6 ui-monospace, monospace; background: #edf1f7; border-radius: 4px; padding: 2px 4px; }
|
|
273
397
|
.appilots-chat .ap-markdown pre { margin: 10px 0; padding: 12px; background: #edf1f7; border-radius: 8px; overflow-x: auto; white-space: pre; }
|
|
274
398
|
.appilots-chat .ap-markdown pre code { padding: 0; }
|
|
275
399
|
.appilots-chat .ap-markdown a { color: #1d4ed8; text-decoration: underline; }
|
|
276
400
|
.appilots-chat .ap-markdown > :first-child { margin-top: 0; }
|
|
277
401
|
.appilots-chat .ap-markdown > :last-child { margin-bottom: 0; }
|
|
278
402
|
.appilots-chat .ap-confirm { border: 1px solid #eddbb4; background: #fffdf7; border-radius: 14px; padding: 14px; }
|
|
279
|
-
.appilots-chat .ap-confirm-title { color: #8c641d; font-size:
|
|
403
|
+
.appilots-chat .ap-confirm-title { color: #8c641d; font-size: 0.6875rem; font-weight: 600; margin-bottom: 6px; }
|
|
404
|
+
.appilots-chat .ap-confirm-expired { margin: 10px 0 0; color: #9a3412; font-size: 0.8125rem; font-weight: 600; }
|
|
405
|
+
.appilots-chat .ap-confirm-actions button { min-height: 44px; min-width: 44px; }
|
|
280
406
|
.appilots-chat .ap-confirm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
|
|
281
407
|
.appilots-chat .ap-primary, .appilots-chat .ap-primary:hover:not(:disabled) { background: var(--ap-accent); border-color: transparent; color: #fff; }
|
|
282
|
-
.appilots-chat .ap-
|
|
408
|
+
.appilots-chat .ap-sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
409
|
+
.appilots-chat .ap-loading { display: flex; align-items: center; gap: 8px; color: #5f6b7d; font-size: 0.75rem; }
|
|
283
410
|
.appilots-chat .ap-loading::before { content: ''; width: 6px; height: 6px; background: var(--ap-accent); border-radius: 50%; }
|
|
284
|
-
.appilots-chat .ap-
|
|
285
|
-
.appilots-chat .ap-
|
|
411
|
+
.appilots-chat .ap-mission { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 14px 8px; }
|
|
412
|
+
.appilots-chat .ap-mission button { font-size: 0.75rem; min-height: 44px; }
|
|
413
|
+
.appilots-chat .ap-error { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 14px; margin: 0 14px 8px; background: #fef2f2; color: #b42318; border-radius: 10px; font-size: 0.75rem; }
|
|
414
|
+
.appilots-chat .ap-latest { align-self: center; font-size: 0.75rem; margin: 0 0 8px; }
|
|
286
415
|
.appilots-chat .ap-footer { background: #fff; border-top: 1px solid #e8ecf2; padding: 14px; }
|
|
287
|
-
.appilots-chat .ap-composer { display: flex; align-items: flex-end; gap: 8px; padding: 8px; border: 1px solid #
|
|
416
|
+
.appilots-chat .ap-composer { display: flex; align-items: flex-end; gap: 8px; padding: 8px; border: 1px solid #838ea0; border-radius: 16px; background: #fff; }
|
|
288
417
|
.appilots-chat .ap-composer:focus-within { border-color: var(--ap-accent); box-shadow: 0 0 0 2px #2563eb0b; }
|
|
289
|
-
.appilots-chat textarea { display: block; flex: 1; min-width: 0; min-height:
|
|
418
|
+
.appilots-chat textarea { display: block; flex: 1; min-width: 0; min-height: 2.5rem; max-height: 7.5rem; margin: 0; padding: 9px 4px; border: 0; border-radius: 4px; background: transparent; color: inherit; font: inherit; line-height: 1.375rem; resize: none; }
|
|
290
419
|
.appilots-chat textarea:focus-visible { outline: none; }
|
|
291
|
-
.appilots-chat .ap-send { width:
|
|
292
|
-
.appilots-chat .ap-hint { display: block; text-align: center; font-size:
|
|
420
|
+
.appilots-chat .ap-send { min-width: 44px; min-height: 44px; padding: 0; border-radius: 12px; flex-shrink: 0; }
|
|
421
|
+
.appilots-chat .ap-hint { display: block; text-align: center; font-size: 0.625rem; color: #5f6b7d; padding-top: 8px; }
|
|
422
|
+
@media (prefers-reduced-motion: reduce) {
|
|
423
|
+
.appilots-chat *, .appilots-chat *::before, .appilots-chat *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
|
|
424
|
+
}
|
|
425
|
+
.appilots-chat .ap-log:focus-visible { outline: 2px solid var(--ap-accent); outline-offset: -2px; }
|
|
426
|
+
.appilots-chat .ap-mic[aria-pressed="true"] { color: #fff; background: #b42318; border-radius: 12px; }
|
|
427
|
+
.appilots-chat .ap-mic[aria-pressed="true"]:hover:not(:disabled) { background: #9a1c13; }
|
|
293
428
|
`;
|
|
294
|
-
function Icon({
|
|
429
|
+
function Icon({
|
|
430
|
+
name
|
|
431
|
+
}) {
|
|
295
432
|
const paths = {
|
|
296
433
|
chat: "M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6A8.4 8.4 0 0 1 12.5 3H13a8.5 8.5 0 0 1 8 8v.5Z",
|
|
297
434
|
human: "M3 14v-3a9 9 0 0 1 18 0v3M3 12h3v8H3zM18 12h3v8h-3zM18 20c0 2-3 2-6 2",
|
|
298
435
|
new: "M12 5v14M5 12h14",
|
|
299
436
|
send: "M12 19V5M5 12l7-7 7 7",
|
|
300
437
|
stop: "M6 6h12v12H6z",
|
|
301
|
-
close: "M6 6l12 12M18 6 6 18"
|
|
438
|
+
close: "M6 6l12 12M18 6 6 18",
|
|
439
|
+
mic: "M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3ZM5 11a7 7 0 0 0 14 0M12 18v3"
|
|
302
440
|
};
|
|
303
441
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
304
442
|
"svg",
|
|
@@ -316,13 +454,24 @@ function Icon({ name }) {
|
|
|
316
454
|
}
|
|
317
455
|
);
|
|
318
456
|
}
|
|
457
|
+
function useStillWorkingSeconds(active) {
|
|
458
|
+
const [seconds, setSeconds] = react.useState(0);
|
|
459
|
+
react.useEffect(() => {
|
|
460
|
+
setSeconds(0);
|
|
461
|
+
if (!active) return;
|
|
462
|
+
return chunkH4ITA2QP_js.startStillWorkingTimer(setSeconds);
|
|
463
|
+
}, [active]);
|
|
464
|
+
return seconds;
|
|
465
|
+
}
|
|
319
466
|
function AppilotsChat({
|
|
320
467
|
title,
|
|
321
468
|
placeholder,
|
|
322
469
|
locale = "en",
|
|
323
470
|
suggestedPrompts = [],
|
|
324
471
|
style,
|
|
325
|
-
onClose
|
|
472
|
+
onClose,
|
|
473
|
+
speech,
|
|
474
|
+
hidden
|
|
326
475
|
}) {
|
|
327
476
|
const strings = chatStrings[locale];
|
|
328
477
|
const chatTitle = title ?? strings.title;
|
|
@@ -332,6 +481,10 @@ function AppilotsChat({
|
|
|
332
481
|
loadingStatusKey,
|
|
333
482
|
error,
|
|
334
483
|
escalation,
|
|
484
|
+
mission,
|
|
485
|
+
pauseMission,
|
|
486
|
+
resumeMission,
|
|
487
|
+
cancelMission,
|
|
335
488
|
sendMessage,
|
|
336
489
|
cancelMessage,
|
|
337
490
|
clearMessages,
|
|
@@ -353,7 +506,45 @@ function AppilotsChat({
|
|
|
353
506
|
const inputRef = react.useRef(null);
|
|
354
507
|
const logRef = react.useRef(null);
|
|
355
508
|
const followMessages = react.useRef(true);
|
|
509
|
+
const hintId = react.useId();
|
|
356
510
|
const confirms = pendingActions.filter((action) => action.type === "confirm");
|
|
511
|
+
const actionStatus = useActionStatus(locale);
|
|
512
|
+
const working = isLoading && confirms.length === 0;
|
|
513
|
+
const stillWorkingSeconds = useStillWorkingSeconds(working);
|
|
514
|
+
const statusText = stillWorkingSeconds ? strings.stillWorking(stillWorkingSeconds) : strings[loadingStatusKey];
|
|
515
|
+
const activeMission = mission && !["completed", "cancelled"].includes(mission.status) ? mission : null;
|
|
516
|
+
const streaming = isLoading && !!messages.at(-1)?.id.startsWith("streaming_");
|
|
517
|
+
const focusInput = () => inputRef.current?.focus();
|
|
518
|
+
const [speechHost, setSpeechHost] = react.useState(null);
|
|
519
|
+
react.useEffect(() => {
|
|
520
|
+
setSpeechHost(
|
|
521
|
+
speech === "browser" ? chunkH4ITA2QP_js.createWebSpeechHost() : chunkH4ITA2QP_js.isSpeechHost(speech) ? speech : null
|
|
522
|
+
);
|
|
523
|
+
}, [speech]);
|
|
524
|
+
const draftBeforeVoice = react.useRef("");
|
|
525
|
+
const voice = useVoiceInput({
|
|
526
|
+
host: speechHost,
|
|
527
|
+
locale,
|
|
528
|
+
onText: (text) => setDraft([draftBeforeVoice.current.trimEnd(), text.trim()].filter(Boolean).join(" "))
|
|
529
|
+
});
|
|
530
|
+
const capturing = voice.state === "starting" || voice.state === "listening";
|
|
531
|
+
const { cancel: cancelCapture } = voice;
|
|
532
|
+
react.useEffect(() => {
|
|
533
|
+
if (hidden) cancelCapture();
|
|
534
|
+
}, [hidden, cancelCapture]);
|
|
535
|
+
const heardOnce = react.useRef(false);
|
|
536
|
+
if (voice.state === "listening") heardOnce.current = true;
|
|
537
|
+
const voiceStatus = voice.state === "listening" ? strings.voiceListening : voice.state === "error" ? strings.voiceError : voice.state === "idle" && heardOnce.current ? strings.voiceStopped : "";
|
|
538
|
+
const toggleVoice = () => {
|
|
539
|
+
if (!capturing) draftBeforeVoice.current = draft;
|
|
540
|
+
voice.toggle();
|
|
541
|
+
};
|
|
542
|
+
const cancelVoice = () => {
|
|
543
|
+
if (!capturing) return false;
|
|
544
|
+
voice.cancel();
|
|
545
|
+
setDraft(draftBeforeVoice.current);
|
|
546
|
+
return true;
|
|
547
|
+
};
|
|
357
548
|
const scrollToLatest = () => {
|
|
358
549
|
followMessages.current = true;
|
|
359
550
|
if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
|
|
@@ -368,11 +559,12 @@ function AppilotsChat({
|
|
|
368
559
|
const input = inputRef.current;
|
|
369
560
|
if (!input) return;
|
|
370
561
|
input.style.height = "auto";
|
|
371
|
-
input.style.height = `${
|
|
562
|
+
input.style.height = `${input.scrollHeight}px`;
|
|
372
563
|
}, [draft]);
|
|
373
564
|
const send = () => {
|
|
374
565
|
const content = draft.trim();
|
|
375
|
-
if (!content || isLoading || requestingHuman) return;
|
|
566
|
+
if (!content || isLoading || requestingHuman || activeMission) return;
|
|
567
|
+
if (voice.state !== "idle" && voice.state !== "error") voice.cancel();
|
|
376
568
|
scrollToLatest();
|
|
377
569
|
clearError();
|
|
378
570
|
setDraft("");
|
|
@@ -388,6 +580,7 @@ function AppilotsChat({
|
|
|
388
580
|
await requestHuman(reason);
|
|
389
581
|
} finally {
|
|
390
582
|
setRequestingHuman(false);
|
|
583
|
+
focusInput();
|
|
391
584
|
}
|
|
392
585
|
};
|
|
393
586
|
return (
|
|
@@ -395,7 +588,7 @@ function AppilotsChat({
|
|
|
395
588
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
396
589
|
"section",
|
|
397
590
|
{
|
|
398
|
-
...{ [
|
|
591
|
+
...{ [chunkH4ITA2QP_js.APPILOTS_CHAT_ATTR]: "true" },
|
|
399
592
|
className: "appilots-chat",
|
|
400
593
|
style,
|
|
401
594
|
"aria-label": chatTitle,
|
|
@@ -405,7 +598,7 @@ function AppilotsChat({
|
|
|
405
598
|
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "ap-header", children: [
|
|
406
599
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-avatar", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: escalation ? "human" : "chat" }) }),
|
|
407
600
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ap-identity", children: [
|
|
408
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
601
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "ap-title", children: chatTitle }),
|
|
409
602
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ap-caption", children: escalation?.operatorName ?? (escalation ? strings.humanLabel : strings.subtitle) })
|
|
410
603
|
] }),
|
|
411
604
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -418,6 +611,7 @@ function AppilotsChat({
|
|
|
418
611
|
disabled: requestingHuman || !messages.length && !escalation && !error,
|
|
419
612
|
onClick: () => {
|
|
420
613
|
clearMessages();
|
|
614
|
+
voice.cancel();
|
|
421
615
|
setDraft("");
|
|
422
616
|
scrollToLatest();
|
|
423
617
|
inputRef.current?.focus();
|
|
@@ -462,8 +656,10 @@ function AppilotsChat({
|
|
|
462
656
|
className: "ap-log",
|
|
463
657
|
ref: logRef,
|
|
464
658
|
role: "log",
|
|
465
|
-
"aria-label":
|
|
466
|
-
"aria-live": "polite",
|
|
659
|
+
"aria-label": strings.messages,
|
|
660
|
+
"aria-live": streaming ? "off" : "polite",
|
|
661
|
+
"aria-busy": streaming,
|
|
662
|
+
tabIndex: 0,
|
|
467
663
|
"data-testid": "appilots-chat-log",
|
|
468
664
|
onScroll: () => {
|
|
469
665
|
const log = logRef.current;
|
|
@@ -474,7 +670,7 @@ function AppilotsChat({
|
|
|
474
670
|
children: [
|
|
475
671
|
messages.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ap-empty", children: [
|
|
476
672
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-avatar", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chat" }) }),
|
|
477
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
673
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { children: strings.welcome }),
|
|
478
674
|
/* @__PURE__ */ jsxRuntime.jsx("p", { children: strings.welcomeBody }),
|
|
479
675
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-suggestions", children: suggestedPrompts.map((prompt) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
480
676
|
"button",
|
|
@@ -513,8 +709,18 @@ function AppilotsChat({
|
|
|
513
709
|
className: `ap-message ap-message-${message.role}`,
|
|
514
710
|
"data-testid": `appilots-message-${message.role}`,
|
|
515
711
|
children: [
|
|
516
|
-
message.role
|
|
517
|
-
|
|
712
|
+
message.role === "user" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ap-sr-only", children: [
|
|
713
|
+
strings.you,
|
|
714
|
+
": "
|
|
715
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ap-sender", children: message.role === "human_agent" ? escalation?.operatorName ?? strings.humanLabel : chatTitle }),
|
|
716
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-bubble", children: message.role === "user" ? message.content : /* @__PURE__ */ jsxRuntime.jsx(
|
|
717
|
+
ChatMarkdown,
|
|
718
|
+
{
|
|
719
|
+
content: message.content,
|
|
720
|
+
hiddenValue: strings.hiddenValue,
|
|
721
|
+
newTab: strings.newTab
|
|
722
|
+
}
|
|
723
|
+
) }),
|
|
518
724
|
/* @__PURE__ */ jsxRuntime.jsx("time", { dateTime: new Date(message.timestamp).toISOString(), children: new Date(message.timestamp).toLocaleTimeString(locale, {
|
|
519
725
|
hour: "2-digit",
|
|
520
726
|
minute: "2-digit"
|
|
@@ -524,50 +730,88 @@ function AppilotsChat({
|
|
|
524
730
|
message.id
|
|
525
731
|
)
|
|
526
732
|
),
|
|
527
|
-
confirms.map((action) =>
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
733
|
+
confirms.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
734
|
+
ConfirmCard,
|
|
735
|
+
{
|
|
736
|
+
action,
|
|
737
|
+
strings,
|
|
738
|
+
composerRef: inputRef,
|
|
739
|
+
onApprove: () => void approveAction(action.id),
|
|
740
|
+
onReject: () => void rejectAction(action.id)
|
|
741
|
+
},
|
|
742
|
+
action.id
|
|
743
|
+
)),
|
|
744
|
+
(isLoading || activeMission) && // While loading this is visual only: the log is a live region, and a
|
|
745
|
+
// second one nested in it double-speaks. The status below is what
|
|
746
|
+
// readers hear. An idle open task has nothing else speaking.
|
|
747
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
748
|
+
"div",
|
|
749
|
+
{
|
|
750
|
+
className: "ap-loading",
|
|
751
|
+
"aria-hidden": isLoading ? "true" : void 0,
|
|
752
|
+
role: isLoading ? void 0 : "status",
|
|
753
|
+
"data-testid": isLoading ? "appilots-loading" : "appilots-mission-progress",
|
|
754
|
+
children: activeMission ? `${activeMission.progressText}${activeMission.status === "paused" ? ` \xB7 ${strings.missionPaused}` : ""}` : statusText
|
|
755
|
+
}
|
|
756
|
+
)
|
|
757
|
+
]
|
|
758
|
+
}
|
|
759
|
+
),
|
|
760
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-sr-only", role: "status", "data-testid": "appilots-chat-status", children: working ? statusText : "" }),
|
|
761
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-sr-only", role: "status", "data-testid": "appilots-action-status", children: actionStatus }),
|
|
762
|
+
showLatest && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
763
|
+
"button",
|
|
764
|
+
{
|
|
765
|
+
type: "button",
|
|
766
|
+
className: "ap-latest",
|
|
767
|
+
onClick: () => {
|
|
768
|
+
scrollToLatest();
|
|
769
|
+
focusInput();
|
|
770
|
+
},
|
|
771
|
+
children: [
|
|
772
|
+
strings.latest,
|
|
773
|
+
" \u2193"
|
|
774
|
+
]
|
|
775
|
+
}
|
|
776
|
+
),
|
|
777
|
+
activeMission && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
778
|
+
"div",
|
|
779
|
+
{
|
|
780
|
+
className: "ap-mission",
|
|
781
|
+
role: "group",
|
|
782
|
+
"aria-label": strings.missionControls,
|
|
783
|
+
"data-testid": "appilots-mission-controls",
|
|
784
|
+
children: [
|
|
785
|
+
activeMission.status === "running" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
786
|
+
"button",
|
|
787
|
+
{
|
|
788
|
+
type: "button",
|
|
789
|
+
"data-testid": "appilots-mission-pause",
|
|
790
|
+
onClick: () => void pauseMission(),
|
|
791
|
+
children: strings.missionPause
|
|
792
|
+
}
|
|
793
|
+
) : activeMission.status !== "awaiting_approval" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
794
|
+
"button",
|
|
795
|
+
{
|
|
796
|
+
type: "button",
|
|
797
|
+
"data-testid": "appilots-mission-resume",
|
|
798
|
+
disabled: isLoading,
|
|
799
|
+
onClick: () => void resumeMission(),
|
|
800
|
+
children: strings.missionResume
|
|
801
|
+
}
|
|
802
|
+
) : null,
|
|
803
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
804
|
+
"button",
|
|
805
|
+
{
|
|
806
|
+
type: "button",
|
|
807
|
+
"data-testid": "appilots-mission-cancel",
|
|
808
|
+
onClick: () => void cancelMission(),
|
|
809
|
+
children: strings.missionCancel
|
|
810
|
+
}
|
|
811
|
+
)
|
|
564
812
|
]
|
|
565
813
|
}
|
|
566
814
|
),
|
|
567
|
-
showLatest && /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "ap-latest", onClick: scrollToLatest, children: [
|
|
568
|
-
strings.latest,
|
|
569
|
-
" \u2193"
|
|
570
|
-
] }),
|
|
571
815
|
error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ap-error", role: "alert", children: [
|
|
572
816
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: error }),
|
|
573
817
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -576,7 +820,10 @@ function AppilotsChat({
|
|
|
576
820
|
type: "button",
|
|
577
821
|
className: "ap-icon",
|
|
578
822
|
"aria-label": strings.dismiss,
|
|
579
|
-
onClick:
|
|
823
|
+
onClick: () => {
|
|
824
|
+
clearError();
|
|
825
|
+
focusInput();
|
|
826
|
+
},
|
|
580
827
|
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "close" })
|
|
581
828
|
}
|
|
582
829
|
)
|
|
@@ -599,9 +846,15 @@ function AppilotsChat({
|
|
|
599
846
|
value: draft,
|
|
600
847
|
placeholder: placeholder ?? strings.placeholder,
|
|
601
848
|
"aria-label": placeholder ?? strings.placeholder,
|
|
849
|
+
"aria-describedby": hintId,
|
|
602
850
|
"data-testid": "appilots-chat-input",
|
|
851
|
+
readOnly: capturing || voice.state === "stopping",
|
|
603
852
|
onChange: (event) => setDraft(event.target.value),
|
|
604
853
|
onKeyDown: (event) => {
|
|
854
|
+
if (event.key === "Escape" && cancelVoice()) {
|
|
855
|
+
event.preventDefault();
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
605
858
|
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing && event.keyCode !== 229) {
|
|
606
859
|
event.preventDefault();
|
|
607
860
|
send();
|
|
@@ -609,6 +862,22 @@ function AppilotsChat({
|
|
|
609
862
|
}
|
|
610
863
|
}
|
|
611
864
|
),
|
|
865
|
+
speechHost && /* @__PURE__ */ jsxRuntime.jsx(
|
|
866
|
+
"button",
|
|
867
|
+
{
|
|
868
|
+
type: "button",
|
|
869
|
+
className: "ap-send ap-mic",
|
|
870
|
+
"data-testid": "appilots-mic-button",
|
|
871
|
+
"aria-label": strings.voice,
|
|
872
|
+
title: strings.voice,
|
|
873
|
+
"aria-pressed": capturing,
|
|
874
|
+
onClick: toggleVoice,
|
|
875
|
+
onKeyDown: (event) => {
|
|
876
|
+
if (event.key === "Escape" && cancelVoice()) event.preventDefault();
|
|
877
|
+
},
|
|
878
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "mic" })
|
|
879
|
+
}
|
|
880
|
+
),
|
|
612
881
|
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
613
882
|
"button",
|
|
614
883
|
{
|
|
@@ -616,7 +885,11 @@ function AppilotsChat({
|
|
|
616
885
|
className: "ap-send ap-primary",
|
|
617
886
|
"aria-label": strings.stop,
|
|
618
887
|
title: strings.stop,
|
|
619
|
-
|
|
888
|
+
"data-testid": "appilots-chat-stop",
|
|
889
|
+
onClick: () => {
|
|
890
|
+
cancelMessage();
|
|
891
|
+
focusInput();
|
|
892
|
+
},
|
|
620
893
|
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "stop" })
|
|
621
894
|
}
|
|
622
895
|
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -627,31 +900,134 @@ function AppilotsChat({
|
|
|
627
900
|
"data-testid": "appilots-chat-send",
|
|
628
901
|
"aria-label": strings.send,
|
|
629
902
|
title: strings.send,
|
|
630
|
-
disabled: !draft.trim() || requestingHuman,
|
|
903
|
+
disabled: !draft.trim() || requestingHuman || !!activeMission,
|
|
631
904
|
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "send" })
|
|
632
905
|
}
|
|
633
906
|
)
|
|
634
907
|
]
|
|
635
908
|
}
|
|
636
909
|
),
|
|
637
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ap-hint", children: strings.inputHint })
|
|
910
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ap-hint", id: hintId, children: strings.inputHint }),
|
|
911
|
+
speechHost && /* @__PURE__ */ jsxRuntime.jsx(
|
|
912
|
+
"span",
|
|
913
|
+
{
|
|
914
|
+
className: voiceStatus ? "ap-hint" : "ap-sr-only",
|
|
915
|
+
role: "status",
|
|
916
|
+
"data-testid": "appilots-voice-status",
|
|
917
|
+
children: voiceStatus
|
|
918
|
+
}
|
|
919
|
+
)
|
|
638
920
|
] })
|
|
639
921
|
]
|
|
640
922
|
}
|
|
641
923
|
)
|
|
642
924
|
);
|
|
643
925
|
}
|
|
926
|
+
function ConfirmCard({
|
|
927
|
+
action,
|
|
928
|
+
strings,
|
|
929
|
+
composerRef,
|
|
930
|
+
onApprove,
|
|
931
|
+
onReject
|
|
932
|
+
}) {
|
|
933
|
+
const id = react.useId();
|
|
934
|
+
const cardRef = react.useRef(null);
|
|
935
|
+
const cancelRef = react.useRef(null);
|
|
936
|
+
const expiresAt = chunkH4ITA2QP_js.confirmExpiresAt(action);
|
|
937
|
+
const [expired, setExpired] = react.useState(() => expiresAt !== void 0 && expiresAt <= Date.now());
|
|
938
|
+
const { message, confirmLabel, cancelLabel, confirmName, cancelName } = deriveConfirmCardCopy(
|
|
939
|
+
action.payload ?? {},
|
|
940
|
+
{
|
|
941
|
+
message: strings.confirmMessage,
|
|
942
|
+
confirmLabel: strings.confirm,
|
|
943
|
+
cancelLabel: strings.cancel,
|
|
944
|
+
describe: strings.confirmDescription
|
|
945
|
+
}
|
|
946
|
+
);
|
|
947
|
+
react.useLayoutEffect(() => {
|
|
948
|
+
cancelRef.current?.focus();
|
|
949
|
+
const card = cardRef.current;
|
|
950
|
+
return () => {
|
|
951
|
+
if (card?.contains(document.activeElement)) composerRef.current?.focus();
|
|
952
|
+
};
|
|
953
|
+
}, [composerRef]);
|
|
954
|
+
react.useEffect(() => {
|
|
955
|
+
if (expiresAt === void 0 || expired) return;
|
|
956
|
+
const timer = setTimeout(() => setExpired(true), expiresAt - Date.now());
|
|
957
|
+
return () => clearTimeout(timer);
|
|
958
|
+
}, [expiresAt, expired]);
|
|
959
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
960
|
+
"div",
|
|
961
|
+
{
|
|
962
|
+
ref: cardRef,
|
|
963
|
+
className: "ap-confirm",
|
|
964
|
+
"data-testid": "appilots-confirm",
|
|
965
|
+
role: "alertdialog",
|
|
966
|
+
"aria-labelledby": `${id}-title`,
|
|
967
|
+
"aria-describedby": `${id}-message`,
|
|
968
|
+
onKeyDown: (event) => {
|
|
969
|
+
if (event.key !== "Escape" || event.nativeEvent.isComposing) return;
|
|
970
|
+
event.preventDefault();
|
|
971
|
+
event.stopPropagation();
|
|
972
|
+
onReject();
|
|
973
|
+
},
|
|
974
|
+
children: [
|
|
975
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-confirm-title", id: `${id}-title`, children: strings.approval }),
|
|
976
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { id: `${id}-message`, children: /* @__PURE__ */ jsxRuntime.jsx(ChatMarkdown, { content: message, hiddenValue: strings.hiddenValue, newTab: strings.newTab }) }),
|
|
977
|
+
expired && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ap-confirm-expired", role: "alert", "data-testid": "appilots-confirm-expired", children: strings.confirmExpired }),
|
|
978
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ap-confirm-actions", children: [
|
|
979
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
980
|
+
"button",
|
|
981
|
+
{
|
|
982
|
+
type: "button",
|
|
983
|
+
className: "ap-primary",
|
|
984
|
+
"data-testid": "appilots-confirm-accept",
|
|
985
|
+
"aria-label": confirmName,
|
|
986
|
+
disabled: expired,
|
|
987
|
+
onClick: onApprove,
|
|
988
|
+
children: confirmLabel
|
|
989
|
+
}
|
|
990
|
+
),
|
|
991
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
992
|
+
"button",
|
|
993
|
+
{
|
|
994
|
+
ref: cancelRef,
|
|
995
|
+
type: "button",
|
|
996
|
+
"data-testid": "appilots-confirm-reject",
|
|
997
|
+
"aria-label": cancelName,
|
|
998
|
+
onClick: onReject,
|
|
999
|
+
children: cancelLabel
|
|
1000
|
+
}
|
|
1001
|
+
)
|
|
1002
|
+
] })
|
|
1003
|
+
]
|
|
1004
|
+
}
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
644
1007
|
var styles = `
|
|
645
1008
|
.appilots-chat-fab { position: fixed; bottom: max(16px, env(safe-area-inset-bottom)); right: max(16px, env(safe-area-inset-right)); display: flex; flex-direction: column; align-items: flex-end; gap: 12px; }
|
|
646
1009
|
.appilots-chat-fab[data-position="bottom-left"] { right: auto; left: max(16px, env(safe-area-inset-left)); align-items: flex-start; }
|
|
647
|
-
.appilots-chat-fab .ap-fab-panel { width: min(
|
|
1010
|
+
.appilots-chat-fab .ap-fab-panel { width: min(24.375rem, calc(100vw - max(16px, env(safe-area-inset-left)) - max(16px, env(safe-area-inset-right)))); height: min(40rem, calc(100dvh - 68px - max(16px, env(safe-area-inset-top)) - max(16px, env(safe-area-inset-bottom)))); }
|
|
648
1011
|
.appilots-chat-fab .ap-fab-panel[hidden] { display: none; }
|
|
649
1012
|
.appilots-chat-fab .appilots-chat { min-height: 0; box-shadow: 0 16px 64px #17203326; }
|
|
650
1013
|
.appilots-chat-fab .ap-fab-button { box-sizing: border-box; display: grid; place-items: center; width: 56px; height: 56px; flex-shrink: 0; padding: 0; margin: 0; border: 0; border-radius: 50%; color: #fff; background: var(--ap-fab-accent); box-shadow: 0 6px 24px #17203333; cursor: pointer; }
|
|
651
1014
|
.appilots-chat-fab .ap-fab-button svg { width: 24px; height: 24px; }
|
|
652
1015
|
.appilots-chat-fab .ap-fab-button:hover { filter: brightness(.94); }
|
|
1016
|
+
.appilots-chat-fab .ap-fab-stop { display: flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 16px; border: 1px solid #d5dbe5; border-radius: 22px; color: #172033; background: #fff; font: 600 14px/1.2 system-ui, -apple-system, sans-serif; box-shadow: 0 4px 16px #17203322; cursor: pointer; }
|
|
1017
|
+
.appilots-chat-fab .ap-fab-stop:focus-visible { outline: 3px solid var(--ap-fab-accent); outline-offset: 2px; }
|
|
653
1018
|
.appilots-chat-fab .ap-fab-button:focus-visible { outline: 3px solid var(--ap-fab-accent); outline-offset: 4px; }
|
|
1019
|
+
.appilots-chat-fab .ap-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
1020
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1021
|
+
.appilots-chat-fab, .appilots-chat-fab * { animation: none !important; transition: none !important; }
|
|
1022
|
+
}
|
|
654
1023
|
`;
|
|
1024
|
+
function ClosedPanelActionStatus({
|
|
1025
|
+
open,
|
|
1026
|
+
locale
|
|
1027
|
+
}) {
|
|
1028
|
+
const actionStatus = useActionStatus(locale);
|
|
1029
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-sr-only", role: "status", "data-testid": "appilots-fab-action-status", children: open ? "" : actionStatus });
|
|
1030
|
+
}
|
|
655
1031
|
function AppilotsChatFab({
|
|
656
1032
|
position = "bottom-right",
|
|
657
1033
|
defaultOpen = false,
|
|
@@ -683,7 +1059,7 @@ function AppilotsChatFab({
|
|
|
683
1059
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
684
1060
|
"div",
|
|
685
1061
|
{
|
|
686
|
-
...{ [
|
|
1062
|
+
...{ [chunkH4ITA2QP_js.APPILOTS_CHAT_ATTR]: "true" },
|
|
687
1063
|
className: "appilots-chat-fab",
|
|
688
1064
|
"data-position": position,
|
|
689
1065
|
style: { zIndex, "--ap-fab-accent": accentColor },
|
|
@@ -702,6 +1078,8 @@ function AppilotsChatFab({
|
|
|
702
1078
|
className: "ap-fab-panel",
|
|
703
1079
|
ref: panelRef,
|
|
704
1080
|
hidden: !open,
|
|
1081
|
+
role: "dialog",
|
|
1082
|
+
"aria-label": chatProps.title ?? strings.title,
|
|
705
1083
|
"data-testid": "appilots-chat-panel",
|
|
706
1084
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
707
1085
|
AppilotsChat,
|
|
@@ -709,11 +1087,14 @@ function AppilotsChatFab({
|
|
|
709
1087
|
...chatProps,
|
|
710
1088
|
locale,
|
|
711
1089
|
onClose: close,
|
|
1090
|
+
hidden: !open,
|
|
712
1091
|
style: { "--ap-accent": accentColor, ...style }
|
|
713
1092
|
}
|
|
714
1093
|
)
|
|
715
1094
|
}
|
|
716
1095
|
),
|
|
1096
|
+
/* @__PURE__ */ jsxRuntime.jsx(ClosedPanelActionStatus, { open, locale }),
|
|
1097
|
+
!open && /* @__PURE__ */ jsxRuntime.jsx(ClosedPanelTurn, { strings }),
|
|
717
1098
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
718
1099
|
"button",
|
|
719
1100
|
{
|
|
@@ -722,6 +1103,7 @@ function AppilotsChatFab({
|
|
|
722
1103
|
ref: triggerRef,
|
|
723
1104
|
"aria-label": open ? strings.closeChat : strings.openChat,
|
|
724
1105
|
title: open ? strings.closeChat : strings.openChat,
|
|
1106
|
+
"aria-haspopup": "dialog",
|
|
725
1107
|
"aria-expanded": open,
|
|
726
1108
|
"aria-controls": panelId,
|
|
727
1109
|
"data-testid": "appilots-chat-fab",
|
|
@@ -735,10 +1117,43 @@ function AppilotsChatFab({
|
|
|
735
1117
|
host
|
|
736
1118
|
);
|
|
737
1119
|
}
|
|
1120
|
+
function ClosedPanelTurn({ strings }) {
|
|
1121
|
+
const { chat } = useAppilotsRuntime();
|
|
1122
|
+
const { isLoading, loadingStatusKey } = react.useSyncExternalStore(
|
|
1123
|
+
chat.subscribeState,
|
|
1124
|
+
chat.getState,
|
|
1125
|
+
chat.getState
|
|
1126
|
+
);
|
|
1127
|
+
const stillWorkingSeconds = useStillWorkingSeconds(isLoading);
|
|
1128
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1129
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1130
|
+
"button",
|
|
1131
|
+
{
|
|
1132
|
+
type: "button",
|
|
1133
|
+
className: "ap-fab-stop",
|
|
1134
|
+
"data-testid": "appilots-fab-stop",
|
|
1135
|
+
onClick: () => chat.cancelMessage(),
|
|
1136
|
+
children: [
|
|
1137
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "stop" }),
|
|
1138
|
+
strings.stop
|
|
1139
|
+
]
|
|
1140
|
+
}
|
|
1141
|
+
),
|
|
1142
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ap-sr-only", role: "status", "data-testid": "appilots-fab-status", children: isLoading ? stillWorkingSeconds ? strings.stillWorking(stillWorkingSeconds) : strings[loadingStatusKey] : "" })
|
|
1143
|
+
] });
|
|
1144
|
+
}
|
|
738
1145
|
|
|
739
1146
|
Object.defineProperty(exports, "AppilotsWebRuntime", {
|
|
740
1147
|
enumerable: true,
|
|
741
|
-
get: function () { return
|
|
1148
|
+
get: function () { return chunkH4ITA2QP_js.AppilotsWebRuntime; }
|
|
1149
|
+
});
|
|
1150
|
+
Object.defineProperty(exports, "createWebSpeechHost", {
|
|
1151
|
+
enumerable: true,
|
|
1152
|
+
get: function () { return chunkH4ITA2QP_js.createWebSpeechHost; }
|
|
1153
|
+
});
|
|
1154
|
+
Object.defineProperty(exports, "isWebSpeechSupported", {
|
|
1155
|
+
enumerable: true,
|
|
1156
|
+
get: function () { return chunkH4ITA2QP_js.isWebSpeechSupported; }
|
|
742
1157
|
});
|
|
743
1158
|
exports.AppilotsChat = AppilotsChat;
|
|
744
1159
|
exports.AppilotsChatFab = AppilotsChatFab;
|