@apteva/apteva-kit 0.1.111 → 0.1.113
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/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +448 -159
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +382 -93
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
|
|
6
6
|
// src/components/Chat/Chat.tsx
|
|
7
|
-
import { useState as useState9, useEffect as
|
|
7
|
+
import { useState as useState9, useEffect as useEffect9, useRef as useRef9, useMemo as useMemo2, useCallback as useCallback4, forwardRef, useImperativeHandle } from "react";
|
|
8
8
|
|
|
9
9
|
// src/components/Chat/MessageList.tsx
|
|
10
10
|
import { useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
@@ -1439,7 +1439,74 @@ function Table({ widget, onAction }) {
|
|
|
1439
1439
|
|
|
1440
1440
|
// src/components/Widgets/widget-library/Form.tsx
|
|
1441
1441
|
import { useState as useState3, useRef as useRef3, useEffect as useEffect3, useCallback } from "react";
|
|
1442
|
+
import { createPortal } from "react-dom";
|
|
1442
1443
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1444
|
+
function PortalDropdown({
|
|
1445
|
+
anchorRef,
|
|
1446
|
+
open,
|
|
1447
|
+
onClose,
|
|
1448
|
+
width,
|
|
1449
|
+
children
|
|
1450
|
+
}) {
|
|
1451
|
+
const dropdownRef = useRef3(null);
|
|
1452
|
+
const [pos, setPos] = useState3({ top: 0, left: 0, width: 0 });
|
|
1453
|
+
useEffect3(() => {
|
|
1454
|
+
if (!open || !anchorRef.current) return;
|
|
1455
|
+
const update = () => {
|
|
1456
|
+
const rect = anchorRef.current.getBoundingClientRect();
|
|
1457
|
+
setPos({
|
|
1458
|
+
top: rect.bottom + window.scrollY + 4,
|
|
1459
|
+
left: rect.left + window.scrollX,
|
|
1460
|
+
width: rect.width
|
|
1461
|
+
});
|
|
1462
|
+
};
|
|
1463
|
+
update();
|
|
1464
|
+
window.addEventListener("scroll", update, true);
|
|
1465
|
+
window.addEventListener("resize", update);
|
|
1466
|
+
return () => {
|
|
1467
|
+
window.removeEventListener("scroll", update, true);
|
|
1468
|
+
window.removeEventListener("resize", update);
|
|
1469
|
+
};
|
|
1470
|
+
}, [open, anchorRef]);
|
|
1471
|
+
useEffect3(() => {
|
|
1472
|
+
if (!open) return;
|
|
1473
|
+
const handler = (e) => {
|
|
1474
|
+
const target = e.target;
|
|
1475
|
+
if (dropdownRef.current && !dropdownRef.current.contains(target) && anchorRef.current && !anchorRef.current.contains(target)) {
|
|
1476
|
+
onClose();
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
document.addEventListener("mousedown", handler);
|
|
1480
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
1481
|
+
}, [open, onClose, anchorRef]);
|
|
1482
|
+
useEffect3(() => {
|
|
1483
|
+
if (!open) return;
|
|
1484
|
+
const handler = (e) => {
|
|
1485
|
+
if (e.key === "Escape") onClose();
|
|
1486
|
+
};
|
|
1487
|
+
document.addEventListener("keydown", handler);
|
|
1488
|
+
return () => document.removeEventListener("keydown", handler);
|
|
1489
|
+
}, [open, onClose]);
|
|
1490
|
+
if (!open) return null;
|
|
1491
|
+
return createPortal(
|
|
1492
|
+
/* @__PURE__ */ jsx6(
|
|
1493
|
+
"div",
|
|
1494
|
+
{
|
|
1495
|
+
ref: dropdownRef,
|
|
1496
|
+
className: "apteva-widget",
|
|
1497
|
+
style: {
|
|
1498
|
+
position: "absolute",
|
|
1499
|
+
top: pos.top,
|
|
1500
|
+
left: pos.left,
|
|
1501
|
+
width: width === "match" ? pos.width : void 0,
|
|
1502
|
+
zIndex: 99999
|
|
1503
|
+
},
|
|
1504
|
+
children
|
|
1505
|
+
}
|
|
1506
|
+
),
|
|
1507
|
+
document.body
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1443
1510
|
function CustomSelect({
|
|
1444
1511
|
name,
|
|
1445
1512
|
value,
|
|
@@ -1449,17 +1516,10 @@ function CustomSelect({
|
|
|
1449
1516
|
onChange
|
|
1450
1517
|
}) {
|
|
1451
1518
|
const [open, setOpen] = useState3(false);
|
|
1452
|
-
const
|
|
1519
|
+
const triggerRef = useRef3(null);
|
|
1453
1520
|
const selected = options?.find((o) => o.value === value);
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
const handler = (e) => {
|
|
1457
|
-
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
1458
|
-
};
|
|
1459
|
-
document.addEventListener("mousedown", handler);
|
|
1460
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
1461
|
-
}, [open]);
|
|
1462
|
-
return /* @__PURE__ */ jsxs4("div", { ref, className: "apteva-select relative", children: [
|
|
1521
|
+
const close = useCallback(() => setOpen(false), []);
|
|
1522
|
+
return /* @__PURE__ */ jsxs4("div", { className: "apteva-select relative", children: [
|
|
1463
1523
|
/* @__PURE__ */ jsxs4(
|
|
1464
1524
|
"select",
|
|
1465
1525
|
{
|
|
@@ -1480,6 +1540,7 @@ function CustomSelect({
|
|
|
1480
1540
|
/* @__PURE__ */ jsxs4(
|
|
1481
1541
|
"button",
|
|
1482
1542
|
{
|
|
1543
|
+
ref: triggerRef,
|
|
1483
1544
|
type: "button",
|
|
1484
1545
|
onClick: () => setOpen(!open),
|
|
1485
1546
|
className: "apteva-select-trigger w-full flex items-center justify-between px-3 py-2 rounded-lg border transition-colors text-left",
|
|
@@ -1498,14 +1559,14 @@ function CustomSelect({
|
|
|
1498
1559
|
]
|
|
1499
1560
|
}
|
|
1500
1561
|
),
|
|
1501
|
-
open
|
|
1562
|
+
/* @__PURE__ */ jsx6(PortalDropdown, { anchorRef: triggerRef, open, onClose: close, width: "match", children: /* @__PURE__ */ jsx6("div", { className: "apteva-select-dropdown rounded-lg border shadow-lg overflow-hidden", children: /* @__PURE__ */ jsxs4("div", { className: "max-h-48 overflow-y-auto py-1", children: [
|
|
1502
1563
|
options?.map((opt) => /* @__PURE__ */ jsx6(
|
|
1503
1564
|
"button",
|
|
1504
1565
|
{
|
|
1505
1566
|
type: "button",
|
|
1506
1567
|
onClick: () => {
|
|
1507
1568
|
onChange(opt.value);
|
|
1508
|
-
|
|
1569
|
+
close();
|
|
1509
1570
|
},
|
|
1510
1571
|
className: `apteva-select-option w-full text-left px-3 py-2 text-sm transition-colors ${opt.value === value ? "apteva-select-option-active" : ""}`,
|
|
1511
1572
|
children: opt.label
|
|
@@ -1513,7 +1574,7 @@ function CustomSelect({
|
|
|
1513
1574
|
opt.value
|
|
1514
1575
|
)),
|
|
1515
1576
|
(!options || options.length === 0) && /* @__PURE__ */ jsx6("div", { className: "px-3 py-2 text-sm apteva-select-placeholder", children: "No options" })
|
|
1516
|
-
] }) })
|
|
1577
|
+
] }) }) })
|
|
1517
1578
|
] });
|
|
1518
1579
|
}
|
|
1519
1580
|
function CustomDatePicker({
|
|
@@ -1524,7 +1585,8 @@ function CustomDatePicker({
|
|
|
1524
1585
|
onChange
|
|
1525
1586
|
}) {
|
|
1526
1587
|
const [open, setOpen] = useState3(false);
|
|
1527
|
-
const
|
|
1588
|
+
const triggerRef = useRef3(null);
|
|
1589
|
+
const close = useCallback(() => setOpen(false), []);
|
|
1528
1590
|
const parseDate = (v) => {
|
|
1529
1591
|
if (v) {
|
|
1530
1592
|
const [y, m, d] = v.split("-").map(Number);
|
|
@@ -1536,14 +1598,6 @@ function CustomDatePicker({
|
|
|
1536
1598
|
const parsed = parseDate(value);
|
|
1537
1599
|
const [viewYear, setViewYear] = useState3(parsed.year);
|
|
1538
1600
|
const [viewMonth, setViewMonth] = useState3(parsed.month);
|
|
1539
|
-
useEffect3(() => {
|
|
1540
|
-
if (!open) return;
|
|
1541
|
-
const handler = (e) => {
|
|
1542
|
-
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
1543
|
-
};
|
|
1544
|
-
document.addEventListener("mousedown", handler);
|
|
1545
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
1546
|
-
}, [open]);
|
|
1547
1601
|
useEffect3(() => {
|
|
1548
1602
|
if (value) {
|
|
1549
1603
|
const p = parseDate(value);
|
|
@@ -1570,7 +1624,7 @@ function CustomDatePicker({
|
|
|
1570
1624
|
const m = String(viewMonth + 1).padStart(2, "0");
|
|
1571
1625
|
const d = String(day).padStart(2, "0");
|
|
1572
1626
|
onChange(`${viewYear}-${m}-${d}`);
|
|
1573
|
-
|
|
1627
|
+
close();
|
|
1574
1628
|
};
|
|
1575
1629
|
const formatDisplay = (v) => {
|
|
1576
1630
|
if (!v) return "";
|
|
@@ -1588,7 +1642,7 @@ function CustomDatePicker({
|
|
|
1588
1642
|
const now = /* @__PURE__ */ new Date();
|
|
1589
1643
|
return now.getFullYear() === viewYear && now.getMonth() === viewMonth && now.getDate() === day;
|
|
1590
1644
|
};
|
|
1591
|
-
return /* @__PURE__ */ jsxs4("div", {
|
|
1645
|
+
return /* @__PURE__ */ jsxs4("div", { className: "apteva-datepicker relative", children: [
|
|
1592
1646
|
/* @__PURE__ */ jsx6(
|
|
1593
1647
|
"input",
|
|
1594
1648
|
{
|
|
@@ -1606,6 +1660,7 @@ function CustomDatePicker({
|
|
|
1606
1660
|
/* @__PURE__ */ jsxs4(
|
|
1607
1661
|
"button",
|
|
1608
1662
|
{
|
|
1663
|
+
ref: triggerRef,
|
|
1609
1664
|
type: "button",
|
|
1610
1665
|
onClick: () => setOpen(!open),
|
|
1611
1666
|
className: "apteva-datepicker-trigger w-full flex items-center justify-between px-3 py-2 rounded-lg border transition-colors text-left",
|
|
@@ -1615,7 +1670,7 @@ function CustomDatePicker({
|
|
|
1615
1670
|
]
|
|
1616
1671
|
}
|
|
1617
1672
|
),
|
|
1618
|
-
open
|
|
1673
|
+
/* @__PURE__ */ jsx6(PortalDropdown, { anchorRef: triggerRef, open, onClose: close, width: "auto", children: /* @__PURE__ */ jsxs4("div", { className: "apteva-datepicker-dropdown rounded-lg border shadow-lg overflow-hidden", children: [
|
|
1619
1674
|
/* @__PURE__ */ jsxs4("div", { className: "apteva-datepicker-header flex items-center justify-between px-3 py-2", children: [
|
|
1620
1675
|
/* @__PURE__ */ jsx6("button", { type: "button", onClick: prevMonth, className: "apteva-datepicker-nav p-1 rounded transition-colors", children: /* @__PURE__ */ jsx6("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx6("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }),
|
|
1621
1676
|
/* @__PURE__ */ jsxs4("span", { className: "apteva-datepicker-title text-sm font-semibold", children: [
|
|
@@ -1648,7 +1703,7 @@ function CustomDatePicker({
|
|
|
1648
1703
|
);
|
|
1649
1704
|
})
|
|
1650
1705
|
] })
|
|
1651
|
-
] })
|
|
1706
|
+
] }) })
|
|
1652
1707
|
] });
|
|
1653
1708
|
}
|
|
1654
1709
|
function Form({ widget, onAction }) {
|
|
@@ -3281,9 +3336,13 @@ function MessageList({
|
|
|
3281
3336
|
}
|
|
3282
3337
|
|
|
3283
3338
|
// src/components/Chat/Composer.tsx
|
|
3284
|
-
import { useState as useState6, useRef as useRef7 } from "react";
|
|
3339
|
+
import { useState as useState6, useEffect as useEffect8, useCallback as useCallback3, useRef as useRef7 } from "react";
|
|
3285
3340
|
import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3286
|
-
|
|
3341
|
+
var getSpeechRecognition = () => {
|
|
3342
|
+
if (typeof window === "undefined") return null;
|
|
3343
|
+
return window.SpeechRecognition || window.webkitSpeechRecognition || null;
|
|
3344
|
+
};
|
|
3345
|
+
function Composer({ onSendMessage, placeholder = "Type a message...", disabled = false, isLoading = false, onStop, onFileUpload, onSwitchMode, speechToText }) {
|
|
3287
3346
|
const [text, setText] = useState6("");
|
|
3288
3347
|
const [showMenu, setShowMenu] = useState6(false);
|
|
3289
3348
|
const [pendingFiles, setPendingFiles] = useState6([]);
|
|
@@ -3292,6 +3351,190 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
|
|
|
3292
3351
|
const textareaRef = useRef7(null);
|
|
3293
3352
|
const fileInputRef = useRef7(null);
|
|
3294
3353
|
const menuButtonRef = useRef7(null);
|
|
3354
|
+
const [isRecording, setIsRecording] = useState6(false);
|
|
3355
|
+
const [recordingTime, setRecordingTime] = useState6(0);
|
|
3356
|
+
const [transcriptFlash, setTranscriptFlash] = useState6(null);
|
|
3357
|
+
const recognitionRef = useRef7(null);
|
|
3358
|
+
const mediaStreamRef = useRef7(null);
|
|
3359
|
+
const audioContextRef = useRef7(null);
|
|
3360
|
+
const analyserRef = useRef7(null);
|
|
3361
|
+
const canvasRef = useRef7(null);
|
|
3362
|
+
const animFrameRef = useRef7(0);
|
|
3363
|
+
const silenceTimerRef = useRef7(null);
|
|
3364
|
+
const recordingTimerRef = useRef7(null);
|
|
3365
|
+
const finalTranscriptRef = useRef7("");
|
|
3366
|
+
const manualStopRef = useRef7(false);
|
|
3367
|
+
const sttConfig = speechToText ? typeof speechToText === "object" ? speechToText : {} : null;
|
|
3368
|
+
const sttSupported = !!sttConfig && !!getSpeechRecognition();
|
|
3369
|
+
const silenceTimeout = sttConfig?.silenceTimeout ?? 2e3;
|
|
3370
|
+
const autoSend = sttConfig?.autoSend !== false;
|
|
3371
|
+
useEffect8(() => {
|
|
3372
|
+
return () => {
|
|
3373
|
+
stopRecording(true);
|
|
3374
|
+
};
|
|
3375
|
+
}, []);
|
|
3376
|
+
const drawWaveform = useCallback3(() => {
|
|
3377
|
+
const canvas = canvasRef.current;
|
|
3378
|
+
const analyser = analyserRef.current;
|
|
3379
|
+
if (!canvas || !analyser) return;
|
|
3380
|
+
const ctx = canvas.getContext("2d");
|
|
3381
|
+
if (!ctx) return;
|
|
3382
|
+
const bufferLength = analyser.frequencyBinCount;
|
|
3383
|
+
const dataArray = new Uint8Array(bufferLength);
|
|
3384
|
+
const draw = () => {
|
|
3385
|
+
animFrameRef.current = requestAnimationFrame(draw);
|
|
3386
|
+
analyser.getByteTimeDomainData(dataArray);
|
|
3387
|
+
const { width, height } = canvas;
|
|
3388
|
+
ctx.clearRect(0, 0, width, height);
|
|
3389
|
+
const barCount = 32;
|
|
3390
|
+
const barWidth = Math.max(2, (width - (barCount - 1) * 2) / barCount);
|
|
3391
|
+
const gap = 2;
|
|
3392
|
+
const samplesPerBar = Math.floor(bufferLength / barCount);
|
|
3393
|
+
for (let i = 0; i < barCount; i++) {
|
|
3394
|
+
let sum = 0;
|
|
3395
|
+
for (let j = 0; j < samplesPerBar; j++) {
|
|
3396
|
+
const val = dataArray[i * samplesPerBar + j] - 128;
|
|
3397
|
+
sum += Math.abs(val);
|
|
3398
|
+
}
|
|
3399
|
+
const avg = sum / samplesPerBar;
|
|
3400
|
+
const barHeight = Math.max(3, avg / 128 * height * 2.5);
|
|
3401
|
+
const x = i * (barWidth + gap);
|
|
3402
|
+
const y = (height - barHeight) / 2;
|
|
3403
|
+
ctx.fillStyle = "#3b82f6";
|
|
3404
|
+
ctx.beginPath();
|
|
3405
|
+
ctx.roundRect(x, y, barWidth, barHeight, barWidth / 2);
|
|
3406
|
+
ctx.fill();
|
|
3407
|
+
}
|
|
3408
|
+
};
|
|
3409
|
+
draw();
|
|
3410
|
+
}, []);
|
|
3411
|
+
const startRecording = useCallback3(async () => {
|
|
3412
|
+
const SpeechRecognitionCtor = getSpeechRecognition();
|
|
3413
|
+
if (!SpeechRecognitionCtor) return;
|
|
3414
|
+
try {
|
|
3415
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
3416
|
+
mediaStreamRef.current = stream;
|
|
3417
|
+
const audioCtx = new AudioContext();
|
|
3418
|
+
audioContextRef.current = audioCtx;
|
|
3419
|
+
const source = audioCtx.createMediaStreamSource(stream);
|
|
3420
|
+
const analyser = audioCtx.createAnalyser();
|
|
3421
|
+
analyser.fftSize = 256;
|
|
3422
|
+
source.connect(analyser);
|
|
3423
|
+
analyserRef.current = analyser;
|
|
3424
|
+
const recognition = new SpeechRecognitionCtor();
|
|
3425
|
+
recognition.continuous = true;
|
|
3426
|
+
recognition.interimResults = true;
|
|
3427
|
+
recognition.lang = sttConfig?.language || navigator.language || "en-US";
|
|
3428
|
+
recognitionRef.current = recognition;
|
|
3429
|
+
finalTranscriptRef.current = "";
|
|
3430
|
+
manualStopRef.current = false;
|
|
3431
|
+
recognition.onresult = (event) => {
|
|
3432
|
+
let final = "";
|
|
3433
|
+
let interim = "";
|
|
3434
|
+
for (let i = 0; i < event.results.length; i++) {
|
|
3435
|
+
const result = event.results[i];
|
|
3436
|
+
if (result.isFinal) {
|
|
3437
|
+
final += result[0].transcript;
|
|
3438
|
+
} else {
|
|
3439
|
+
interim += result[0].transcript;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
finalTranscriptRef.current = final;
|
|
3443
|
+
if (silenceTimerRef.current) {
|
|
3444
|
+
clearTimeout(silenceTimerRef.current);
|
|
3445
|
+
}
|
|
3446
|
+
silenceTimerRef.current = setTimeout(() => {
|
|
3447
|
+
stopRecording(false);
|
|
3448
|
+
}, silenceTimeout);
|
|
3449
|
+
};
|
|
3450
|
+
recognition.onerror = (event) => {
|
|
3451
|
+
if (event.error !== "aborted") {
|
|
3452
|
+
console.warn("Speech recognition error:", event.error);
|
|
3453
|
+
}
|
|
3454
|
+
stopRecording(true);
|
|
3455
|
+
};
|
|
3456
|
+
recognition.onend = () => {
|
|
3457
|
+
if (!manualStopRef.current && isRecording) {
|
|
3458
|
+
finishRecording();
|
|
3459
|
+
}
|
|
3460
|
+
};
|
|
3461
|
+
recognition.start();
|
|
3462
|
+
setIsRecording(true);
|
|
3463
|
+
setRecordingTime(0);
|
|
3464
|
+
recordingTimerRef.current = setInterval(() => {
|
|
3465
|
+
setRecordingTime((t) => t + 1);
|
|
3466
|
+
}, 1e3);
|
|
3467
|
+
requestAnimationFrame(() => drawWaveform());
|
|
3468
|
+
silenceTimerRef.current = setTimeout(() => {
|
|
3469
|
+
stopRecording(false);
|
|
3470
|
+
}, silenceTimeout + 1e3);
|
|
3471
|
+
} catch (err) {
|
|
3472
|
+
console.warn("Microphone access denied or error:", err);
|
|
3473
|
+
setFileError("Microphone access denied");
|
|
3474
|
+
setTimeout(() => setFileError(null), 3e3);
|
|
3475
|
+
}
|
|
3476
|
+
}, [sttConfig?.language, silenceTimeout, drawWaveform]);
|
|
3477
|
+
const finishRecording = useCallback3(() => {
|
|
3478
|
+
const transcript = finalTranscriptRef.current.trim();
|
|
3479
|
+
setIsRecording(false);
|
|
3480
|
+
setRecordingTime(0);
|
|
3481
|
+
if (transcript) {
|
|
3482
|
+
if (autoSend) {
|
|
3483
|
+
setTranscriptFlash(transcript);
|
|
3484
|
+
setTimeout(() => {
|
|
3485
|
+
setTranscriptFlash(null);
|
|
3486
|
+
onSendMessage(transcript);
|
|
3487
|
+
}, 600);
|
|
3488
|
+
} else {
|
|
3489
|
+
setText((prev) => prev ? `${prev} ${transcript}` : transcript);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
}, [autoSend, onSendMessage]);
|
|
3493
|
+
const stopRecording = useCallback3((isCleanupOnly) => {
|
|
3494
|
+
manualStopRef.current = true;
|
|
3495
|
+
if (silenceTimerRef.current) {
|
|
3496
|
+
clearTimeout(silenceTimerRef.current);
|
|
3497
|
+
silenceTimerRef.current = null;
|
|
3498
|
+
}
|
|
3499
|
+
if (recordingTimerRef.current) {
|
|
3500
|
+
clearInterval(recordingTimerRef.current);
|
|
3501
|
+
recordingTimerRef.current = null;
|
|
3502
|
+
}
|
|
3503
|
+
if (animFrameRef.current) {
|
|
3504
|
+
cancelAnimationFrame(animFrameRef.current);
|
|
3505
|
+
animFrameRef.current = 0;
|
|
3506
|
+
}
|
|
3507
|
+
if (recognitionRef.current) {
|
|
3508
|
+
try {
|
|
3509
|
+
recognitionRef.current.stop();
|
|
3510
|
+
} catch (_e) {
|
|
3511
|
+
}
|
|
3512
|
+
recognitionRef.current = null;
|
|
3513
|
+
}
|
|
3514
|
+
if (mediaStreamRef.current) {
|
|
3515
|
+
mediaStreamRef.current.getTracks().forEach((t) => t.stop());
|
|
3516
|
+
mediaStreamRef.current = null;
|
|
3517
|
+
}
|
|
3518
|
+
if (audioContextRef.current) {
|
|
3519
|
+
try {
|
|
3520
|
+
audioContextRef.current.close();
|
|
3521
|
+
} catch (_e) {
|
|
3522
|
+
}
|
|
3523
|
+
audioContextRef.current = null;
|
|
3524
|
+
}
|
|
3525
|
+
analyserRef.current = null;
|
|
3526
|
+
if (!isCleanupOnly) {
|
|
3527
|
+
finishRecording();
|
|
3528
|
+
} else {
|
|
3529
|
+
setIsRecording(false);
|
|
3530
|
+
setRecordingTime(0);
|
|
3531
|
+
}
|
|
3532
|
+
}, [finishRecording]);
|
|
3533
|
+
const formatTime = (seconds) => {
|
|
3534
|
+
const m = Math.floor(seconds / 60);
|
|
3535
|
+
const s = seconds % 60;
|
|
3536
|
+
return `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
|
|
3537
|
+
};
|
|
3295
3538
|
const handleKeyDown = (e) => {
|
|
3296
3539
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
3297
3540
|
e.preventDefault();
|
|
@@ -3374,12 +3617,17 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
|
|
|
3374
3617
|
}
|
|
3375
3618
|
return /* @__PURE__ */ jsx23("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx23("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) });
|
|
3376
3619
|
};
|
|
3620
|
+
const hasMic = sttSupported && !isRecording;
|
|
3621
|
+
const gridCols = hasMic ? "auto 1fr auto auto" : "auto 1fr auto";
|
|
3622
|
+
const gridAreas = isRecording ? '"plus waveform waveform stop"' : isMultiLine ? hasMic ? '"textarea textarea textarea textarea" "plus . mic send"' : '"textarea textarea textarea" "plus . send"' : hasMic ? '"plus textarea mic send"' : '"plus textarea send"';
|
|
3623
|
+
const gridColsRecording = "auto 1fr auto";
|
|
3377
3624
|
return /* @__PURE__ */ jsxs17("div", { className: "px-4 py-3 relative", children: [
|
|
3378
3625
|
fileError && /* @__PURE__ */ jsx23("div", { className: "apteva-file-error", children: /* @__PURE__ */ jsxs17("div", { className: "apteva-file-error-content", children: [
|
|
3379
3626
|
/* @__PURE__ */ jsx23("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx23("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
|
|
3380
3627
|
/* @__PURE__ */ jsx23("span", { children: fileError })
|
|
3381
3628
|
] }) }),
|
|
3382
|
-
|
|
3629
|
+
transcriptFlash && /* @__PURE__ */ jsx23("div", { className: "apteva-transcript-flash", children: /* @__PURE__ */ jsx23("span", { children: transcriptFlash }) }),
|
|
3630
|
+
pendingFiles.length > 0 && !isRecording && /* @__PURE__ */ jsx23("div", { className: "apteva-file-preview", children: pendingFiles.map((pf, index) => /* @__PURE__ */ jsxs17("div", { className: "apteva-file-item", children: [
|
|
3383
3631
|
pf.preview ? /* @__PURE__ */ jsx23("img", { src: pf.preview, alt: pf.file.name, className: "apteva-file-thumb" }) : /* @__PURE__ */ jsx23("div", { className: "apteva-file-icon", children: getFileIcon(pf.file.type) }),
|
|
3384
3632
|
/* @__PURE__ */ jsxs17("div", { className: "apteva-file-info", children: [
|
|
3385
3633
|
/* @__PURE__ */ jsx23("span", { className: "apteva-file-name", children: pf.file.name }),
|
|
@@ -3400,12 +3648,12 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
|
|
|
3400
3648
|
{
|
|
3401
3649
|
className: "apteva-composer",
|
|
3402
3650
|
style: {
|
|
3403
|
-
gridTemplateColumns:
|
|
3404
|
-
gridTemplateAreas:
|
|
3651
|
+
gridTemplateColumns: isRecording ? gridColsRecording : gridCols,
|
|
3652
|
+
gridTemplateAreas: isRecording ? '"plus waveform stop"' : gridAreas,
|
|
3405
3653
|
alignItems: "end"
|
|
3406
3654
|
},
|
|
3407
3655
|
children: [
|
|
3408
|
-
/* @__PURE__ */
|
|
3656
|
+
/* @__PURE__ */ jsx23("div", { className: "relative flex-shrink-0 self-end", style: { gridArea: "plus" }, children: isRecording ? /* @__PURE__ */ jsx23("div", { className: "apteva-composer-rec-dot", title: "Recording...", children: /* @__PURE__ */ jsx23("span", {}) }) : /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
3409
3657
|
/* @__PURE__ */ jsx23(
|
|
3410
3658
|
"button",
|
|
3411
3659
|
{
|
|
@@ -3459,39 +3707,77 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
|
|
|
3459
3707
|
}
|
|
3460
3708
|
)
|
|
3461
3709
|
] })
|
|
3462
|
-
] }),
|
|
3463
|
-
/* @__PURE__ */
|
|
3464
|
-
"
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
}
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
}
|
|
3485
|
-
) : /* @__PURE__ */
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3710
|
+
] }) }),
|
|
3711
|
+
isRecording ? /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
3712
|
+
/* @__PURE__ */ jsxs17("div", { className: "apteva-composer-waveform", style: { gridArea: "waveform" }, children: [
|
|
3713
|
+
/* @__PURE__ */ jsx23(
|
|
3714
|
+
"canvas",
|
|
3715
|
+
{
|
|
3716
|
+
ref: canvasRef,
|
|
3717
|
+
width: 300,
|
|
3718
|
+
height: 36,
|
|
3719
|
+
className: "apteva-composer-waveform-canvas"
|
|
3720
|
+
}
|
|
3721
|
+
),
|
|
3722
|
+
/* @__PURE__ */ jsx23("span", { className: "apteva-composer-recording-timer", children: formatTime(recordingTime) })
|
|
3723
|
+
] }),
|
|
3724
|
+
/* @__PURE__ */ jsx23("div", { className: "self-end", style: { gridArea: "stop" }, children: /* @__PURE__ */ jsx23(
|
|
3725
|
+
"button",
|
|
3726
|
+
{
|
|
3727
|
+
onClick: () => stopRecording(false),
|
|
3728
|
+
className: "apteva-composer-stop-btn",
|
|
3729
|
+
title: "Stop recording",
|
|
3730
|
+
children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1", fill: "currentColor" }) })
|
|
3731
|
+
}
|
|
3732
|
+
) })
|
|
3733
|
+
] }) : /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
3734
|
+
/* @__PURE__ */ jsx23(
|
|
3735
|
+
"textarea",
|
|
3736
|
+
{
|
|
3737
|
+
ref: textareaRef,
|
|
3738
|
+
value: text,
|
|
3739
|
+
onChange: handleChange,
|
|
3740
|
+
onKeyDown: handleKeyDown,
|
|
3741
|
+
placeholder,
|
|
3742
|
+
disabled,
|
|
3743
|
+
className: "apteva-composer-textarea resize-none bg-transparent border-none focus:outline-none !text-neutral-900 dark:!text-neutral-100 placeholder-neutral-400 dark:placeholder-neutral-500 py-1 disabled:opacity-50 disabled:cursor-not-allowed overflow-y-auto max-h-[200px]",
|
|
3744
|
+
style: { gridArea: "textarea" },
|
|
3745
|
+
rows: 1
|
|
3746
|
+
}
|
|
3747
|
+
),
|
|
3748
|
+
sttSupported && /* @__PURE__ */ jsx23("div", { className: "self-end", style: { gridArea: "mic" }, children: /* @__PURE__ */ jsx23(
|
|
3749
|
+
"button",
|
|
3750
|
+
{
|
|
3751
|
+
onClick: startRecording,
|
|
3752
|
+
disabled: disabled || isLoading,
|
|
3753
|
+
className: "apteva-composer-mic-btn",
|
|
3754
|
+
title: "Voice input",
|
|
3755
|
+
children: /* @__PURE__ */ jsxs17("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
3756
|
+
/* @__PURE__ */ jsx23("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", fill: "currentColor" }),
|
|
3757
|
+
/* @__PURE__ */ jsx23("path", { d: "M19 10v2a7 7 0 01-14 0v-2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
3758
|
+
/* @__PURE__ */ jsx23("path", { d: "M12 19v4M8 23h8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
|
|
3759
|
+
] })
|
|
3760
|
+
}
|
|
3761
|
+
) }),
|
|
3762
|
+
/* @__PURE__ */ jsx23("div", { className: "self-end", style: { gridArea: "send" }, children: isLoading && onStop ? /* @__PURE__ */ jsx23(
|
|
3763
|
+
"button",
|
|
3764
|
+
{
|
|
3765
|
+
onClick: onStop,
|
|
3766
|
+
className: "apteva-composer-stop-btn",
|
|
3767
|
+
title: "Stop generation",
|
|
3768
|
+
children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1", fill: "currentColor" }) })
|
|
3769
|
+
}
|
|
3770
|
+
) : /* @__PURE__ */ jsx23(
|
|
3771
|
+
"button",
|
|
3772
|
+
{
|
|
3773
|
+
onClick: handleSend,
|
|
3774
|
+
disabled: !text.trim() && pendingFiles.length === 0 || disabled,
|
|
3775
|
+
className: "apteva-composer-send-btn w-8 h-8 rounded-lg flex items-center justify-center font-bold transition-all flex-shrink-0 border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800 !text-neutral-700 dark:!text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-700 disabled:opacity-30 disabled:cursor-not-allowed !text-lg",
|
|
3776
|
+
title: "Send message",
|
|
3777
|
+
children: /* @__PURE__ */ jsx23("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("path", { d: "M8 3L8 13M8 3L4 7M8 3L12 7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
3778
|
+
}
|
|
3779
|
+
) })
|
|
3780
|
+
] })
|
|
3495
3781
|
]
|
|
3496
3782
|
}
|
|
3497
3783
|
),
|
|
@@ -4090,6 +4376,8 @@ var Chat = forwardRef(function Chat2({
|
|
|
4090
4376
|
availableWidgets,
|
|
4091
4377
|
compactWidgetContext = false,
|
|
4092
4378
|
onWidgetRender,
|
|
4379
|
+
// Speech to text
|
|
4380
|
+
speechToText,
|
|
4093
4381
|
className
|
|
4094
4382
|
}, ref) {
|
|
4095
4383
|
const [messages, setMessages] = useState9(initialMessages);
|
|
@@ -4111,7 +4399,7 @@ var Chat = forwardRef(function Chat2({
|
|
|
4111
4399
|
const [showSettingsMenu, setShowSettingsMenu] = useState9(false);
|
|
4112
4400
|
const fileInputRef = useRef9(null);
|
|
4113
4401
|
const [persistentWidgets, setPersistentWidgets] = useState9(/* @__PURE__ */ new Map());
|
|
4114
|
-
const updatePersistentWidgets =
|
|
4402
|
+
const updatePersistentWidgets = useCallback4((msgs) => {
|
|
4115
4403
|
setPersistentWidgets((prev) => {
|
|
4116
4404
|
const next = new Map(prev);
|
|
4117
4405
|
let changed = false;
|
|
@@ -4129,7 +4417,7 @@ var Chat = forwardRef(function Chat2({
|
|
|
4129
4417
|
return changed ? next : prev;
|
|
4130
4418
|
});
|
|
4131
4419
|
}, []);
|
|
4132
|
-
|
|
4420
|
+
useEffect9(() => {
|
|
4133
4421
|
updatePersistentWidgets(messages);
|
|
4134
4422
|
}, [messages, updatePersistentWidgets]);
|
|
4135
4423
|
const persistentWidgetList = useMemo2(() => Array.from(persistentWidgets.values()), [persistentWidgets]);
|
|
@@ -4155,7 +4443,7 @@ var Chat = forwardRef(function Chat2({
|
|
|
4155
4443
|
return context ? `${context}
|
|
4156
4444
|
${widgetContext}` : widgetContext;
|
|
4157
4445
|
}, [context, enableWidgets, availableWidgets, compactWidgetContext]);
|
|
4158
|
-
|
|
4446
|
+
useEffect9(() => {
|
|
4159
4447
|
if (apiUrl || apiKey) {
|
|
4160
4448
|
aptevaClient.configure({
|
|
4161
4449
|
...apiUrl && { apiUrl },
|
|
@@ -4163,15 +4451,15 @@ ${widgetContext}` : widgetContext;
|
|
|
4163
4451
|
});
|
|
4164
4452
|
}
|
|
4165
4453
|
}, [apiUrl, apiKey]);
|
|
4166
|
-
|
|
4454
|
+
useEffect9(() => {
|
|
4167
4455
|
if (threadId) {
|
|
4168
4456
|
onThreadChange?.(threadId);
|
|
4169
4457
|
}
|
|
4170
4458
|
}, [threadId, onThreadChange]);
|
|
4171
|
-
|
|
4459
|
+
useEffect9(() => {
|
|
4172
4460
|
setInternalPlanMode(planMode);
|
|
4173
4461
|
}, [planMode]);
|
|
4174
|
-
|
|
4462
|
+
useEffect9(() => {
|
|
4175
4463
|
const handleClickOutside = (event) => {
|
|
4176
4464
|
const target = event.target;
|
|
4177
4465
|
if (showSettingsMenu && !target.closest(".settings-menu-container")) {
|
|
@@ -4191,7 +4479,7 @@ ${widgetContext}` : widgetContext;
|
|
|
4191
4479
|
}
|
|
4192
4480
|
};
|
|
4193
4481
|
const defaultPlaceholder = mode === "chat" ? "Type a message..." : "Enter your command...";
|
|
4194
|
-
const handleWidgetAction =
|
|
4482
|
+
const handleWidgetAction = useCallback4((action) => {
|
|
4195
4483
|
onAction?.(action);
|
|
4196
4484
|
if (action.type === "submit" && action.payload?.formData) {
|
|
4197
4485
|
const formData = action.payload.formData;
|
|
@@ -4745,7 +5033,8 @@ ${planToExecute}`;
|
|
|
4745
5033
|
isLoading,
|
|
4746
5034
|
onStop: handleStop,
|
|
4747
5035
|
onFileUpload,
|
|
4748
|
-
onSwitchMode: showModeToggle ? () => handleModeChange("command") : void 0
|
|
5036
|
+
onSwitchMode: showModeToggle ? () => handleModeChange("command") : void 0,
|
|
5037
|
+
speechToText
|
|
4749
5038
|
}
|
|
4750
5039
|
)
|
|
4751
5040
|
] }),
|
|
@@ -4792,7 +5081,7 @@ import { useState as useState10 } from "react";
|
|
|
4792
5081
|
import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
4793
5082
|
|
|
4794
5083
|
// src/components/Command/Command.tsx
|
|
4795
|
-
import React, { useState as useState11, useEffect as
|
|
5084
|
+
import React, { useState as useState11, useEffect as useEffect10 } from "react";
|
|
4796
5085
|
import { Fragment as Fragment7, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
4797
5086
|
function Command({
|
|
4798
5087
|
agentId,
|
|
@@ -4833,15 +5122,15 @@ function Command({
|
|
|
4833
5122
|
const [showSettingsMenu, setShowSettingsMenu] = useState11(false);
|
|
4834
5123
|
const [internalPlanMode, setInternalPlanMode] = useState11(planMode);
|
|
4835
5124
|
const fileInputRef = React.useRef(null);
|
|
4836
|
-
|
|
5125
|
+
useEffect10(() => {
|
|
4837
5126
|
if (autoExecute && state === "idle" && command) {
|
|
4838
5127
|
executeCommand();
|
|
4839
5128
|
}
|
|
4840
5129
|
}, [autoExecute]);
|
|
4841
|
-
|
|
5130
|
+
useEffect10(() => {
|
|
4842
5131
|
setInternalPlanMode(planMode);
|
|
4843
5132
|
}, [planMode]);
|
|
4844
|
-
|
|
5133
|
+
useEffect10(() => {
|
|
4845
5134
|
const handleClickOutside = (event) => {
|
|
4846
5135
|
const target = event.target;
|
|
4847
5136
|
if (showSettingsMenu && !target.closest(".settings-menu-container")) {
|
|
@@ -5874,7 +6163,7 @@ function Prompt({
|
|
|
5874
6163
|
}
|
|
5875
6164
|
|
|
5876
6165
|
// src/components/Stream/Stream.tsx
|
|
5877
|
-
import { useState as useState13, useEffect as
|
|
6166
|
+
import { useState as useState13, useEffect as useEffect11 } from "react";
|
|
5878
6167
|
import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
5879
6168
|
function Stream({
|
|
5880
6169
|
agentId,
|
|
@@ -5894,7 +6183,7 @@ function Stream({
|
|
|
5894
6183
|
const [text, setText] = useState13("");
|
|
5895
6184
|
const [isStreaming, setIsStreaming] = useState13(false);
|
|
5896
6185
|
const [isComplete, setIsComplete] = useState13(false);
|
|
5897
|
-
|
|
6186
|
+
useEffect11(() => {
|
|
5898
6187
|
if (autoStart && !isStreaming && !isComplete) {
|
|
5899
6188
|
startStreaming();
|
|
5900
6189
|
}
|
|
@@ -6165,7 +6454,7 @@ function Threads({
|
|
|
6165
6454
|
}
|
|
6166
6455
|
|
|
6167
6456
|
// src/components/AutoInterface/AutoInterface.tsx
|
|
6168
|
-
import { useState as useState16, useRef as useRef10, useCallback as
|
|
6457
|
+
import { useState as useState16, useRef as useRef10, useCallback as useCallback5, useEffect as useEffect12 } from "react";
|
|
6169
6458
|
|
|
6170
6459
|
// src/components/AutoInterface/LayoutRenderer.tsx
|
|
6171
6460
|
import { useState as useState15 } from "react";
|
|
@@ -6443,11 +6732,11 @@ function AutoInterface({
|
|
|
6443
6732
|
generateInterfaceContext(),
|
|
6444
6733
|
context || ""
|
|
6445
6734
|
].filter(Boolean).join("\n\n");
|
|
6446
|
-
const updateInterface =
|
|
6735
|
+
const updateInterface = useCallback5((newSpec) => {
|
|
6447
6736
|
setInterfaceSpec(newSpec);
|
|
6448
6737
|
onInterfaceChange?.(newSpec);
|
|
6449
6738
|
}, [onInterfaceChange]);
|
|
6450
|
-
const handleAction =
|
|
6739
|
+
const handleAction = useCallback5((action) => {
|
|
6451
6740
|
onAction?.(action);
|
|
6452
6741
|
if (chatRef.current) {
|
|
6453
6742
|
chatRef.current.sendMessage(
|
|
@@ -6455,7 +6744,7 @@ function AutoInterface({
|
|
|
6455
6744
|
);
|
|
6456
6745
|
}
|
|
6457
6746
|
}, [onAction]);
|
|
6458
|
-
const handleMessageComplete =
|
|
6747
|
+
const handleMessageComplete = useCallback5((result) => {
|
|
6459
6748
|
if (!result?.data) return;
|
|
6460
6749
|
const text = typeof result.data === "string" ? result.data : result.data.message || "";
|
|
6461
6750
|
console.log("[AutoInterface] Chat message complete, text (" + text.length + " chars):", text.substring(0, 300));
|
|
@@ -6476,7 +6765,7 @@ function AutoInterface({
|
|
|
6476
6765
|
}
|
|
6477
6766
|
setIsGenerating(false);
|
|
6478
6767
|
}, [interfaceSpec, updateInterface]);
|
|
6479
|
-
|
|
6768
|
+
useEffect12(() => {
|
|
6480
6769
|
if (!initialPrompt || initialInterface || useMock) return;
|
|
6481
6770
|
if (!apiUrl) return;
|
|
6482
6771
|
let cancelled = false;
|
|
@@ -6601,29 +6890,29 @@ function getThemeScript() {
|
|
|
6601
6890
|
}
|
|
6602
6891
|
|
|
6603
6892
|
// src/hooks/useInterfaceState.ts
|
|
6604
|
-
import { useState as useState17, useCallback as
|
|
6893
|
+
import { useState as useState17, useCallback as useCallback6 } from "react";
|
|
6605
6894
|
function useInterfaceState(initialSpec) {
|
|
6606
6895
|
const [spec, setSpec] = useState17(initialSpec || null);
|
|
6607
6896
|
const [isStreaming, setIsStreaming] = useState17(false);
|
|
6608
|
-
const setInterface =
|
|
6897
|
+
const setInterface = useCallback6((newSpec) => {
|
|
6609
6898
|
setSpec(newSpec);
|
|
6610
6899
|
}, []);
|
|
6611
|
-
const clearInterface =
|
|
6900
|
+
const clearInterface = useCallback6(() => {
|
|
6612
6901
|
setSpec(null);
|
|
6613
6902
|
}, []);
|
|
6614
|
-
const applyInterfaceUpdate =
|
|
6903
|
+
const applyInterfaceUpdate = useCallback6((update) => {
|
|
6615
6904
|
setSpec((prev) => {
|
|
6616
6905
|
if (!prev) return prev;
|
|
6617
6906
|
return applyUpdate(prev, update);
|
|
6618
6907
|
});
|
|
6619
6908
|
}, []);
|
|
6620
|
-
const applyInterfaceUpdates =
|
|
6909
|
+
const applyInterfaceUpdates = useCallback6((updates) => {
|
|
6621
6910
|
setSpec((prev) => {
|
|
6622
6911
|
if (!prev) return prev;
|
|
6623
6912
|
return applyUpdates(prev, updates);
|
|
6624
6913
|
});
|
|
6625
6914
|
}, []);
|
|
6626
|
-
const getNode =
|
|
6915
|
+
const getNode = useCallback6((id) => {
|
|
6627
6916
|
if (!spec) return null;
|
|
6628
6917
|
return findNode(spec.root, id);
|
|
6629
6918
|
}, [spec]);
|
|
@@ -6640,7 +6929,7 @@ function useInterfaceState(initialSpec) {
|
|
|
6640
6929
|
}
|
|
6641
6930
|
|
|
6642
6931
|
// src/hooks/useInterfaceAI.ts
|
|
6643
|
-
import { useCallback as
|
|
6932
|
+
import { useCallback as useCallback7, useRef as useRef11 } from "react";
|
|
6644
6933
|
function useInterfaceAI({
|
|
6645
6934
|
agentId,
|
|
6646
6935
|
apiUrl,
|
|
@@ -6660,7 +6949,7 @@ function useInterfaceAI({
|
|
|
6660
6949
|
...apiKey && { apiKey }
|
|
6661
6950
|
});
|
|
6662
6951
|
}
|
|
6663
|
-
const sendMessage =
|
|
6952
|
+
const sendMessage = useCallback7(async (message) => {
|
|
6664
6953
|
accumulatedTextRef.current = "";
|
|
6665
6954
|
onStreamStart?.();
|
|
6666
6955
|
const systemPrompt = [
|