@akropolys/kiku 1.6.2 → 1.6.4
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 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +406 -93
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +406 -93
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +196 -3
- package/dist/styles.css.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -20,7 +20,7 @@ var SearchIcon = () => /* @__PURE__ */ jsxs("svg", { width: "15", height: "15",
|
|
|
20
20
|
function SearchBar({
|
|
21
21
|
placeholder = "Search products\u2026",
|
|
22
22
|
limit = 10,
|
|
23
|
-
debounceMs =
|
|
23
|
+
debounceMs = 150,
|
|
24
24
|
onSelect,
|
|
25
25
|
className,
|
|
26
26
|
inputClassName,
|
|
@@ -31,10 +31,8 @@ function SearchBar({
|
|
|
31
31
|
}) {
|
|
32
32
|
const [query, setQuery] = useState("");
|
|
33
33
|
const [open, setOpen] = useState(false);
|
|
34
|
-
const
|
|
35
|
-
const { results, loading, search, clear } = useSearch();
|
|
34
|
+
const { results, loading, search, clear } = useSearch({ debounceMs });
|
|
36
35
|
const client = useAkropolysContext();
|
|
37
|
-
const timer = useRef();
|
|
38
36
|
const wrap = useRef(null);
|
|
39
37
|
const ignoreNextQueryChange = useRef(false);
|
|
40
38
|
useEffect(() => {
|
|
@@ -42,20 +40,13 @@ function SearchBar({
|
|
|
42
40
|
ignoreNextQueryChange.current = false;
|
|
43
41
|
return;
|
|
44
42
|
}
|
|
45
|
-
clearTimeout(timer.current);
|
|
46
43
|
if (!query.trim()) {
|
|
47
44
|
clear();
|
|
48
45
|
setOpen(false);
|
|
49
|
-
setIsDebouncing(false);
|
|
50
46
|
return;
|
|
51
47
|
}
|
|
52
48
|
setOpen(true);
|
|
53
|
-
|
|
54
|
-
timer.current = setTimeout(() => {
|
|
55
|
-
setIsDebouncing(false);
|
|
56
|
-
search(query, limit);
|
|
57
|
-
}, debounceMs);
|
|
58
|
-
return () => clearTimeout(timer.current);
|
|
49
|
+
search(query, limit);
|
|
59
50
|
}, [query]);
|
|
60
51
|
useEffect(() => {
|
|
61
52
|
const h = (e) => {
|
|
@@ -111,8 +102,8 @@ function SearchBar({
|
|
|
111
102
|
}
|
|
112
103
|
),
|
|
113
104
|
showDrop && /* @__PURE__ */ jsxs("div", { className: cn("hsk-sb-drop", classNames.dropdown, dropdownClassName), style: { position: "absolute" }, children: [
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
loading && /* @__PURE__ */ jsx("div", { className: "hsk-sb-loading-bar" }),
|
|
106
|
+
loading && results.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
116
107
|
/* @__PURE__ */ jsxs("div", { className: "hsk-sb-skeleton-row", children: [
|
|
117
108
|
/* @__PURE__ */ jsx("span", { className: "hsk-sb-skeleton-icon" }),
|
|
118
109
|
/* @__PURE__ */ jsxs("div", { className: "hsk-sb-row-body", children: [
|
|
@@ -128,36 +119,52 @@ function SearchBar({
|
|
|
128
119
|
] })
|
|
129
120
|
] })
|
|
130
121
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
131
|
-
results.length === 0 && !loading &&
|
|
122
|
+
results.length === 0 && !loading && /* @__PURE__ */ jsxs("div", { className: "hsk-sb-empty", children: [
|
|
132
123
|
"No results for \u201C",
|
|
133
124
|
query,
|
|
134
125
|
"\u201D"
|
|
135
126
|
] }),
|
|
136
|
-
results.map((r, i) =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
127
|
+
results.map((r, i) => {
|
|
128
|
+
if (renderResult) {
|
|
129
|
+
return /* @__PURE__ */ jsx(
|
|
130
|
+
"div",
|
|
131
|
+
{
|
|
132
|
+
onClick: () => handleSelect(r),
|
|
133
|
+
className: "hsk-sb-fade",
|
|
134
|
+
style: { animationDelay: `${i * 18}ms` },
|
|
135
|
+
children: renderResult(r)
|
|
136
|
+
},
|
|
137
|
+
r.id
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const thumb = r.entity.image ?? r.entity.thumbnail ?? r.entity.images?.[0];
|
|
141
|
+
return /* @__PURE__ */ jsxs(
|
|
142
|
+
"div",
|
|
143
|
+
{
|
|
144
|
+
className: cn("hsk-sb-row hsk-sb-fade", classNames.row),
|
|
145
|
+
style: { animationDelay: `${i * 18}ms` },
|
|
146
|
+
onClick: () => handleSelect(r),
|
|
147
|
+
children: [
|
|
148
|
+
/* @__PURE__ */ jsx("span", { className: "hsk-sb-row-thumb", children: thumb ? /* @__PURE__ */ jsx(
|
|
149
|
+
"img",
|
|
150
|
+
{
|
|
151
|
+
src: thumb,
|
|
152
|
+
alt: "",
|
|
153
|
+
loading: "lazy",
|
|
154
|
+
onError: (e) => {
|
|
155
|
+
e.currentTarget.style.display = "none";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
) : /* @__PURE__ */ jsx(SearchIcon, {}) }),
|
|
159
|
+
/* @__PURE__ */ jsxs("div", { className: "hsk-sb-row-body", children: [
|
|
160
|
+
/* @__PURE__ */ jsx("div", { className: "hsk-sb-row-title", children: r.entity.title ?? r.entity.name }),
|
|
161
|
+
(r.entity.category || r.entity.brand) && /* @__PURE__ */ jsx("div", { className: "hsk-sb-row-sub", children: r.entity.category ?? r.entity.brand })
|
|
162
|
+
] })
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
r.id
|
|
166
|
+
);
|
|
167
|
+
})
|
|
161
168
|
] })
|
|
162
169
|
] })
|
|
163
170
|
] });
|
|
@@ -1167,6 +1174,25 @@ var ShoppingBagIcon = () => /* @__PURE__ */ jsxs6("svg", { width: "13", height:
|
|
|
1167
1174
|
/* @__PURE__ */ jsx7("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
|
|
1168
1175
|
/* @__PURE__ */ jsx7("path", { d: "M16 10a4 4 0 0 1-8 0" })
|
|
1169
1176
|
] });
|
|
1177
|
+
var SecureShieldIcon = ({ className, size = 18 }) => /* @__PURE__ */ jsxs6(
|
|
1178
|
+
"svg",
|
|
1179
|
+
{
|
|
1180
|
+
className,
|
|
1181
|
+
width: size,
|
|
1182
|
+
height: size,
|
|
1183
|
+
viewBox: "0 0 24 24",
|
|
1184
|
+
fill: "none",
|
|
1185
|
+
stroke: "currentColor",
|
|
1186
|
+
strokeWidth: "2.5",
|
|
1187
|
+
strokeLinecap: "round",
|
|
1188
|
+
strokeLinejoin: "round",
|
|
1189
|
+
children: [
|
|
1190
|
+
/* @__PURE__ */ jsx7("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }),
|
|
1191
|
+
/* @__PURE__ */ jsx7("path", { d: "M12 11a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z" }),
|
|
1192
|
+
/* @__PURE__ */ jsx7("path", { d: "M12 11v4" })
|
|
1193
|
+
]
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1170
1196
|
var PaperclipIcon = () => /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }) });
|
|
1171
1197
|
var MicIcon2 = () => /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1172
1198
|
/* @__PURE__ */ jsx7("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z" }),
|
|
@@ -1189,6 +1215,173 @@ var DEFAULT_CHIPS = [
|
|
|
1189
1215
|
];
|
|
1190
1216
|
var SESSIONS_KEY = "akropolys_chat_sessions";
|
|
1191
1217
|
var MAX_SESSIONS = 20;
|
|
1218
|
+
function parseAtKiku(raw) {
|
|
1219
|
+
const trimmed = raw.trim();
|
|
1220
|
+
if (!/^@kiku\b/i.test(trimmed)) return null;
|
|
1221
|
+
const rest = trimmed.slice(5).trim();
|
|
1222
|
+
if (rest === "" || /^(capture|save)\b/i.test(rest)) {
|
|
1223
|
+
return {
|
|
1224
|
+
intent: "capture",
|
|
1225
|
+
cleanQuery: rest.replace(/^(capture|save)\s*/i, "").trim() || trimmed
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
if (/^(history|what have you|show my|my items|what did you|saved|captures|recall)\b/i.test(rest)) {
|
|
1229
|
+
return { intent: "view_history", cleanQuery: "show my saved items" };
|
|
1230
|
+
}
|
|
1231
|
+
if (/^(delete|forget|remove|unsave)\b/i.test(rest)) {
|
|
1232
|
+
return {
|
|
1233
|
+
intent: "delete",
|
|
1234
|
+
cleanQuery: rest.replace(/^(delete|forget|remove|unsave)\s*/i, "").trim() || trimmed
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
return { intent: "capture", cleanQuery: rest || trimmed };
|
|
1238
|
+
}
|
|
1239
|
+
function KikuPickerMenu({
|
|
1240
|
+
sources,
|
|
1241
|
+
referencedIds,
|
|
1242
|
+
defaultCurrency,
|
|
1243
|
+
onCapture,
|
|
1244
|
+
onCaptureAll,
|
|
1245
|
+
onViewHistory,
|
|
1246
|
+
onDelete,
|
|
1247
|
+
onDismiss
|
|
1248
|
+
}) {
|
|
1249
|
+
const discussed = sources.filter((s) => s.id && referencedIds.includes(s.id));
|
|
1250
|
+
return /* @__PURE__ */ jsxs6(
|
|
1251
|
+
"div",
|
|
1252
|
+
{
|
|
1253
|
+
className: "hsk-kiku-picker",
|
|
1254
|
+
role: "menu",
|
|
1255
|
+
"aria-label": "@kiku commands",
|
|
1256
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1257
|
+
children: [
|
|
1258
|
+
/* @__PURE__ */ jsxs6("div", { className: "hsk-kiku-picker-header", children: [
|
|
1259
|
+
/* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-at", children: "@kiku" }),
|
|
1260
|
+
/* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-hint", children: "What would you like to do?" }),
|
|
1261
|
+
/* @__PURE__ */ jsx7("button", { className: "hsk-kiku-picker-close", onClick: onDismiss, "aria-label": "Close", children: /* @__PURE__ */ jsx7(CloseIcon, {}) })
|
|
1262
|
+
] }),
|
|
1263
|
+
discussed.length > 0 && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1264
|
+
/* @__PURE__ */ jsx7("div", { className: "hsk-kiku-picker-section", children: "Discussed in this chat" }),
|
|
1265
|
+
discussed.map((src, i) => /* @__PURE__ */ jsxs6(
|
|
1266
|
+
"button",
|
|
1267
|
+
{
|
|
1268
|
+
className: "hsk-kiku-picker-item",
|
|
1269
|
+
role: "menuitem",
|
|
1270
|
+
onClick: () => {
|
|
1271
|
+
onCapture(src);
|
|
1272
|
+
onDismiss();
|
|
1273
|
+
},
|
|
1274
|
+
children: [
|
|
1275
|
+
src.image ? /* @__PURE__ */ jsx7("img", { src: src.image, alt: src.name, className: "hsk-kiku-picker-img" }) : /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-img-placeholder", children: /* @__PURE__ */ jsx7(SecureShieldIcon, { size: 14 }) }),
|
|
1276
|
+
/* @__PURE__ */ jsxs6("span", { className: "hsk-kiku-picker-item-body", children: [
|
|
1277
|
+
/* @__PURE__ */ jsxs6("span", { className: "hsk-kiku-picker-item-name", children: [
|
|
1278
|
+
"\u{1F4CC} ",
|
|
1279
|
+
src.name
|
|
1280
|
+
] }),
|
|
1281
|
+
src.price && /* @__PURE__ */ jsxs6("span", { className: "hsk-kiku-picker-item-price", children: [
|
|
1282
|
+
src.currency ?? defaultCurrency,
|
|
1283
|
+
" ",
|
|
1284
|
+
parseFloat(String(src.price).replace(/[^0-9.]/g, "") || "0").toLocaleString()
|
|
1285
|
+
] })
|
|
1286
|
+
] })
|
|
1287
|
+
]
|
|
1288
|
+
},
|
|
1289
|
+
src.id ?? i
|
|
1290
|
+
)),
|
|
1291
|
+
discussed.length > 1 && /* @__PURE__ */ jsx7(
|
|
1292
|
+
"button",
|
|
1293
|
+
{
|
|
1294
|
+
className: "hsk-kiku-picker-item hsk-kiku-picker-item--all",
|
|
1295
|
+
role: "menuitem",
|
|
1296
|
+
onClick: () => {
|
|
1297
|
+
onCaptureAll(discussed);
|
|
1298
|
+
onDismiss();
|
|
1299
|
+
},
|
|
1300
|
+
children: /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-body", children: /* @__PURE__ */ jsxs6("span", { className: "hsk-kiku-picker-item-name", children: [
|
|
1301
|
+
"\u{1F4CC} Capture all (",
|
|
1302
|
+
discussed.length,
|
|
1303
|
+
" items)"
|
|
1304
|
+
] }) })
|
|
1305
|
+
}
|
|
1306
|
+
),
|
|
1307
|
+
/* @__PURE__ */ jsx7("div", { className: "hsk-kiku-picker-divider" })
|
|
1308
|
+
] }),
|
|
1309
|
+
discussed.length === 0 && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
1310
|
+
/* @__PURE__ */ jsx7(
|
|
1311
|
+
"button",
|
|
1312
|
+
{
|
|
1313
|
+
className: "hsk-kiku-picker-item",
|
|
1314
|
+
role: "menuitem",
|
|
1315
|
+
onClick: () => {
|
|
1316
|
+
onCapture({ name: "current page", id: void 0 });
|
|
1317
|
+
onDismiss();
|
|
1318
|
+
},
|
|
1319
|
+
children: /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-body", children: /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-name", children: "\u{1F4CC} Capture current page" }) })
|
|
1320
|
+
}
|
|
1321
|
+
),
|
|
1322
|
+
/* @__PURE__ */ jsx7("div", { className: "hsk-kiku-picker-divider" })
|
|
1323
|
+
] }),
|
|
1324
|
+
/* @__PURE__ */ jsx7(
|
|
1325
|
+
"button",
|
|
1326
|
+
{
|
|
1327
|
+
className: "hsk-kiku-picker-item",
|
|
1328
|
+
role: "menuitem",
|
|
1329
|
+
onClick: () => {
|
|
1330
|
+
onViewHistory();
|
|
1331
|
+
onDismiss();
|
|
1332
|
+
},
|
|
1333
|
+
children: /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-body", children: /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-name", children: "\u{1F4CB} What have you saved?" }) })
|
|
1334
|
+
}
|
|
1335
|
+
),
|
|
1336
|
+
/* @__PURE__ */ jsx7(
|
|
1337
|
+
"button",
|
|
1338
|
+
{
|
|
1339
|
+
className: "hsk-kiku-picker-item",
|
|
1340
|
+
role: "menuitem",
|
|
1341
|
+
onClick: () => {
|
|
1342
|
+
onDelete();
|
|
1343
|
+
onDismiss();
|
|
1344
|
+
},
|
|
1345
|
+
children: /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-body", children: /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-name", children: "\u{1F5D1}\uFE0F Delete this" }) })
|
|
1346
|
+
}
|
|
1347
|
+
)
|
|
1348
|
+
]
|
|
1349
|
+
}
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
function AtPickerMenu({ onSelect, onDismiss }) {
|
|
1353
|
+
return /* @__PURE__ */ jsxs6(
|
|
1354
|
+
"div",
|
|
1355
|
+
{
|
|
1356
|
+
className: "hsk-kiku-picker",
|
|
1357
|
+
role: "menu",
|
|
1358
|
+
"aria-label": "Extensions",
|
|
1359
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1360
|
+
children: [
|
|
1361
|
+
/* @__PURE__ */ jsxs6("div", { className: "hsk-kiku-picker-header", children: [
|
|
1362
|
+
/* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-at", children: "@" }),
|
|
1363
|
+
/* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-hint", children: "Use an extension" }),
|
|
1364
|
+
/* @__PURE__ */ jsx7("button", { className: "hsk-kiku-picker-close", onClick: onDismiss, "aria-label": "Close", children: /* @__PURE__ */ jsx7(CloseIcon, {}) })
|
|
1365
|
+
] }),
|
|
1366
|
+
/* @__PURE__ */ jsxs6(
|
|
1367
|
+
"button",
|
|
1368
|
+
{
|
|
1369
|
+
className: "hsk-kiku-picker-item",
|
|
1370
|
+
role: "menuitem",
|
|
1371
|
+
onClick: () => onSelect("@kiku"),
|
|
1372
|
+
children: [
|
|
1373
|
+
/* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-img-placeholder", style: { background: "color-mix(in srgb, var(--hsk-primary, #7c5cfc) 15%, transparent)", color: "var(--hsk-primary, #7c5cfc)" }, children: /* @__PURE__ */ jsx7(SparkleIcon2, {}) }),
|
|
1374
|
+
/* @__PURE__ */ jsxs6("span", { className: "hsk-kiku-picker-item-body", children: [
|
|
1375
|
+
/* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-name", children: "@kiku" }),
|
|
1376
|
+
/* @__PURE__ */ jsx7("span", { className: "hsk-kiku-picker-item-price", style: { fontSize: "11px", whiteSpace: "normal", lineHeight: "1.3" }, children: "Ask Kiku to capture pages, remember items, or show your history" })
|
|
1377
|
+
] })
|
|
1378
|
+
]
|
|
1379
|
+
}
|
|
1380
|
+
)
|
|
1381
|
+
]
|
|
1382
|
+
}
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1192
1385
|
function loadSessions() {
|
|
1193
1386
|
try {
|
|
1194
1387
|
if (typeof window === "undefined") return [];
|
|
@@ -1509,18 +1702,7 @@ var getFriendlyError = (err) => {
|
|
|
1509
1702
|
return str;
|
|
1510
1703
|
}
|
|
1511
1704
|
};
|
|
1512
|
-
var
|
|
1513
|
-
var MK_NOUN = ["thunder", "falcon", "river", "ember", "tiger", "comet", "willow", "onyx"];
|
|
1514
|
-
function generateMagicWord() {
|
|
1515
|
-
return MK_ADJ[Math.floor(Math.random() * MK_ADJ.length)] + MK_NOUN[Math.floor(Math.random() * MK_NOUN.length)];
|
|
1516
|
-
}
|
|
1517
|
-
function parseMemoryKey(raw) {
|
|
1518
|
-
const cleaned = raw.trim().replace(/\s+/g, "");
|
|
1519
|
-
const m = cleaned.match(/^(\d{6,})([a-zA-Z].*)?$/);
|
|
1520
|
-
const phone = m?.[1] || cleaned.replace(/\D/g, "");
|
|
1521
|
-
const word = (m?.[2] || "").toLowerCase() || generateMagicWord();
|
|
1522
|
-
return { phone, word };
|
|
1523
|
-
}
|
|
1705
|
+
var KIKU_KEY_REVEAL_SECONDS = 60;
|
|
1524
1706
|
function parseThinking(text) {
|
|
1525
1707
|
const openMatch = text.match(/<\s*thinking\s*>/i);
|
|
1526
1708
|
if (!openMatch) {
|
|
@@ -1628,6 +1810,7 @@ function ChatModal({
|
|
|
1628
1810
|
theme,
|
|
1629
1811
|
classNames = {},
|
|
1630
1812
|
enableVoice = false,
|
|
1813
|
+
voiceLang,
|
|
1631
1814
|
enableVision = false,
|
|
1632
1815
|
visionCategoryHint
|
|
1633
1816
|
}) {
|
|
@@ -1661,7 +1844,7 @@ function ChatModal({
|
|
|
1661
1844
|
if (!hasSpeechAPI || voiceState !== "idle") return;
|
|
1662
1845
|
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
1663
1846
|
const recognition = new SR();
|
|
1664
|
-
recognition.lang = "en-US";
|
|
1847
|
+
recognition.lang = voiceLang || document.documentElement.lang || navigator.language || "en-US";
|
|
1665
1848
|
recognition.interimResults = true;
|
|
1666
1849
|
recognition.maxAlternatives = 1;
|
|
1667
1850
|
recognitionRef.current = recognition;
|
|
@@ -1694,7 +1877,7 @@ function ChatModal({
|
|
|
1694
1877
|
setVoiceState((prev) => prev === "listening" ? "idle" : prev);
|
|
1695
1878
|
};
|
|
1696
1879
|
recognition.start();
|
|
1697
|
-
}, [hasSpeechAPI, voiceState]);
|
|
1880
|
+
}, [hasSpeechAPI, voiceState, voiceLang]);
|
|
1698
1881
|
const stopVoice = useCallback2(() => {
|
|
1699
1882
|
recognitionRef.current?.stop();
|
|
1700
1883
|
setVoiceState("idle");
|
|
@@ -1709,23 +1892,37 @@ function ChatModal({
|
|
|
1709
1892
|
const [selectedProduct, setSelectedProduct] = useState5(null);
|
|
1710
1893
|
const bottomRef = useRef5(null);
|
|
1711
1894
|
const textareaRef = useRef5(null);
|
|
1712
|
-
const [
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
const [
|
|
1719
|
-
const [memoryKey, setMemoryKey] = useState5(null);
|
|
1895
|
+
const [keyInput, setKeyInput] = useState5("");
|
|
1896
|
+
const [keyPhase, setKeyPhase] = useState5("idle");
|
|
1897
|
+
const [mintedKey, setMintedKey] = useState5(null);
|
|
1898
|
+
const [keyCountdown, setKeyCountdown] = useState5(KIKU_KEY_REVEAL_SECONDS);
|
|
1899
|
+
const [minting, setMinting] = useState5(false);
|
|
1900
|
+
const [showKikuPicker, setShowKikuPicker] = useState5(false);
|
|
1901
|
+
const [showAtPicker, setShowAtPicker] = useState5(false);
|
|
1720
1902
|
const [sessions, setSessions] = useState5(() => loadSessions());
|
|
1721
1903
|
const [sidebarOpen, setSidebarOpen] = useState5(false);
|
|
1722
1904
|
const [replayMessages, setReplayMessages] = useState5(null);
|
|
1723
1905
|
useEffect3(() => {
|
|
1724
1906
|
if (!lastAction) return;
|
|
1725
|
-
if (lastAction.type === "
|
|
1726
|
-
|
|
1907
|
+
if (lastAction.type === "request_kiku_key") {
|
|
1908
|
+
setKeyPhase("prompt_key");
|
|
1727
1909
|
}
|
|
1728
1910
|
}, [lastAction]);
|
|
1911
|
+
useEffect3(() => {
|
|
1912
|
+
if (!mintedKey) return;
|
|
1913
|
+
setKeyCountdown(KIKU_KEY_REVEAL_SECONDS);
|
|
1914
|
+
const t = setInterval(() => {
|
|
1915
|
+
setKeyCountdown((s) => {
|
|
1916
|
+
if (s <= 1) {
|
|
1917
|
+
clearInterval(t);
|
|
1918
|
+
setMintedKey(null);
|
|
1919
|
+
return 0;
|
|
1920
|
+
}
|
|
1921
|
+
return s - 1;
|
|
1922
|
+
});
|
|
1923
|
+
}, 1e3);
|
|
1924
|
+
return () => clearInterval(t);
|
|
1925
|
+
}, [mintedKey]);
|
|
1729
1926
|
const isStringTheme = typeof theme === "string";
|
|
1730
1927
|
const hskThemeAttr = isStringTheme ? theme : void 0;
|
|
1731
1928
|
const customStyles = !isStringTheme && theme ? {
|
|
@@ -1735,16 +1932,30 @@ function ChatModal({
|
|
|
1735
1932
|
...theme?.fontFamily && { "--hsk-font": theme.fontFamily },
|
|
1736
1933
|
...theme?.borderRadius && { "--hsk-border-radius": theme.borderRadius }
|
|
1737
1934
|
} : void 0;
|
|
1738
|
-
const
|
|
1739
|
-
if (!phoneInput.trim()) return;
|
|
1740
|
-
const { phone, word } = parseMemoryKey(phoneInput);
|
|
1741
|
-
if (!phone) return;
|
|
1742
|
-
if (typeof window !== "undefined") localStorage.setItem("akropolys_user_phone", phone);
|
|
1743
|
-
client.setShopperIdentity(phone, word);
|
|
1744
|
-
setMemoryKey(phone + word);
|
|
1745
|
-
setPaymentPhase("idle");
|
|
1935
|
+
const retryLastMessage = async () => {
|
|
1746
1936
|
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
1747
|
-
if (lastUserMsg) await
|
|
1937
|
+
if (lastUserMsg) await handleSend(lastUserMsg.content);
|
|
1938
|
+
};
|
|
1939
|
+
const handleUseExistingKey = async () => {
|
|
1940
|
+
const key = keyInput.trim();
|
|
1941
|
+
if (!key) return;
|
|
1942
|
+
client.setKikuKey(key);
|
|
1943
|
+
setKeyInput("");
|
|
1944
|
+
setKeyPhase("idle");
|
|
1945
|
+
await retryLastMessage();
|
|
1946
|
+
};
|
|
1947
|
+
const handleCreateKey = async () => {
|
|
1948
|
+
if (minting) return;
|
|
1949
|
+
setMinting(true);
|
|
1950
|
+
try {
|
|
1951
|
+
const key = await client.mintKikuKey();
|
|
1952
|
+
setMintedKey(key);
|
|
1953
|
+
setKeyPhase("idle");
|
|
1954
|
+
await retryLastMessage();
|
|
1955
|
+
} catch {
|
|
1956
|
+
} finally {
|
|
1957
|
+
setMinting(false);
|
|
1958
|
+
}
|
|
1748
1959
|
};
|
|
1749
1960
|
const msgsContainerRef = useRef5(null);
|
|
1750
1961
|
useEffect3(() => {
|
|
@@ -1779,7 +1990,7 @@ function ChatModal({
|
|
|
1779
1990
|
saveCurrentSession();
|
|
1780
1991
|
reset();
|
|
1781
1992
|
setReplayMessages(null);
|
|
1782
|
-
|
|
1993
|
+
setKeyPhase("idle");
|
|
1783
1994
|
}, [reset, saveCurrentSession]);
|
|
1784
1995
|
const handleSourceClick = (src) => {
|
|
1785
1996
|
setSelectedProduct(src);
|
|
@@ -1787,27 +1998,97 @@ function ChatModal({
|
|
|
1787
1998
|
const q = `Tell me more about the ${src.name}${src.price ? ` (${src.currency ?? defaultCurrency} ${src.price})` : ""} \u2014 what are its key specs, who is it best for, and is it worth buying?`;
|
|
1788
1999
|
send(q);
|
|
1789
2000
|
};
|
|
1790
|
-
const handleSend = async (text, extraAttachments) => {
|
|
1791
|
-
const
|
|
1792
|
-
if (!
|
|
2001
|
+
const handleSend = async (text, extraAttachments, forcedIntent, captureTargets) => {
|
|
2002
|
+
const raw = (text ?? input).trim();
|
|
2003
|
+
if (!raw || loading) return;
|
|
2004
|
+
const kiku = parseAtKiku(raw);
|
|
2005
|
+
const q = kiku ? kiku.cleanQuery : raw;
|
|
2006
|
+
const resolvedForcedIntent = forcedIntent ?? kiku?.intent;
|
|
1793
2007
|
setReplayMessages(null);
|
|
1794
2008
|
setSelectedProduct(null);
|
|
2009
|
+
setShowKikuPicker(false);
|
|
2010
|
+
setShowAtPicker(false);
|
|
1795
2011
|
setInput("");
|
|
1796
2012
|
if (textareaRef.current) {
|
|
1797
2013
|
textareaRef.current.style.height = "auto";
|
|
1798
2014
|
}
|
|
1799
2015
|
const toSend = extraAttachments ?? attachments;
|
|
1800
2016
|
setAttachments([]);
|
|
1801
|
-
await send(q,
|
|
2017
|
+
await send(q, raw, toSend.length > 0 ? toSend : void 0, resolvedForcedIntent, captureTargets);
|
|
1802
2018
|
};
|
|
2019
|
+
const handleSelectExtension = (ext) => {
|
|
2020
|
+
setInput(ext + " ");
|
|
2021
|
+
setShowAtPicker(false);
|
|
2022
|
+
setShowKikuPicker(true);
|
|
2023
|
+
if (textareaRef.current) {
|
|
2024
|
+
textareaRef.current.focus();
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
const handleKikuCapture = useCallback2((product) => {
|
|
2028
|
+
const name = product.name || "";
|
|
2029
|
+
const display = `@kiku capture${name ? " " + name : ""}`;
|
|
2030
|
+
const q = name || "capture current page";
|
|
2031
|
+
setInput("");
|
|
2032
|
+
setReplayMessages(null);
|
|
2033
|
+
setSelectedProduct(null);
|
|
2034
|
+
const toSend = attachments;
|
|
2035
|
+
setAttachments([]);
|
|
2036
|
+
send(q, display, toSend.length > 0 ? toSend : void 0, "capture");
|
|
2037
|
+
}, [attachments, send]);
|
|
2038
|
+
const handleKikuCaptureAll = useCallback2((products) => {
|
|
2039
|
+
const targets = products.filter((p) => p.id).map((p) => ({
|
|
2040
|
+
name: p.name || "",
|
|
2041
|
+
url: p.url || "",
|
|
2042
|
+
image: p.image || "",
|
|
2043
|
+
price: p.price ? String(p.price) : "",
|
|
2044
|
+
currency: p.currency || defaultCurrency
|
|
2045
|
+
}));
|
|
2046
|
+
const names = products.map((p) => p.name).filter(Boolean).join(", ");
|
|
2047
|
+
const display = `@kiku capture all (${products.length} items)`;
|
|
2048
|
+
setInput("");
|
|
2049
|
+
setReplayMessages(null);
|
|
2050
|
+
setSelectedProduct(null);
|
|
2051
|
+
setAttachments([]);
|
|
2052
|
+
send(names || "capture all", display, void 0, "capture_all", targets);
|
|
2053
|
+
}, [defaultCurrency, send]);
|
|
2054
|
+
const handleKikuViewHistory = useCallback2(() => {
|
|
2055
|
+
const display = "@kiku what have you saved?";
|
|
2056
|
+
setInput("");
|
|
2057
|
+
setReplayMessages(null);
|
|
2058
|
+
setSelectedProduct(null);
|
|
2059
|
+
setAttachments([]);
|
|
2060
|
+
send("show my saved items", display, void 0, "view_history");
|
|
2061
|
+
}, [send]);
|
|
2062
|
+
const handleKikuDelete = useCallback2(() => {
|
|
2063
|
+
const display = "@kiku delete this";
|
|
2064
|
+
setInput("");
|
|
2065
|
+
setReplayMessages(null);
|
|
2066
|
+
setSelectedProduct(null);
|
|
2067
|
+
setAttachments([]);
|
|
2068
|
+
send("delete this", display, void 0, "delete");
|
|
2069
|
+
}, [send]);
|
|
1803
2070
|
const handleKeyDown = (e) => {
|
|
2071
|
+
if (e.key === "Escape" && showKikuPicker) {
|
|
2072
|
+
e.preventDefault();
|
|
2073
|
+
setShowKikuPicker(false);
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
if (e.key === "Escape" && showAtPicker) {
|
|
2077
|
+
e.preventDefault();
|
|
2078
|
+
setShowAtPicker(false);
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
1804
2081
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
1805
2082
|
e.preventDefault();
|
|
1806
2083
|
handleSend();
|
|
1807
2084
|
}
|
|
1808
2085
|
};
|
|
1809
2086
|
const handleInput = (e) => {
|
|
1810
|
-
|
|
2087
|
+
const val = e.target.value;
|
|
2088
|
+
setInput(val);
|
|
2089
|
+
const trimmed = val.trim();
|
|
2090
|
+
setShowAtPicker(trimmed === "@");
|
|
2091
|
+
setShowKikuPicker(/^@kiku\b/i.test(trimmed));
|
|
1811
2092
|
const t = e.target;
|
|
1812
2093
|
t.style.height = "auto";
|
|
1813
2094
|
t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
|
|
@@ -1944,7 +2225,10 @@ function ChatModal({
|
|
|
1944
2225
|
const displayContent = !isUser && isLast && lastIntent === "compare" && sources.length >= 2 && !replayMessages ? stripMarkdownTables(msg.content) : msg.content;
|
|
1945
2226
|
return /* @__PURE__ */ jsx7("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-user-msg", children: [
|
|
1946
2227
|
msg.images && msg.images.length > 0 && /* @__PURE__ */ jsx7("div", { className: "hsk-cb-user-imgs", children: msg.images.map((img, i) => /* @__PURE__ */ jsx7("img", { src: img, alt: `attachment ${i + 1}`, className: "hsk-cb-user-img-thumb" }, i)) }),
|
|
1947
|
-
msg.content && /* @__PURE__ */
|
|
2228
|
+
msg.content && /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-user-bubble", children: [
|
|
2229
|
+
/^@kiku\b/i.test(msg.content) && /* @__PURE__ */ jsx7("span", { className: "hsk-kiku-badge", children: "@kiku" }),
|
|
2230
|
+
msg.content
|
|
2231
|
+
] })
|
|
1948
2232
|
] }) : /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-ai-msg", children: [
|
|
1949
2233
|
/* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx7(SparkleIcon2, {}) }),
|
|
1950
2234
|
/* @__PURE__ */ jsxs6("div", { className: "hsk-cb-ai-body", children: [
|
|
@@ -1959,7 +2243,7 @@ function ChatModal({
|
|
|
1959
2243
|
] });
|
|
1960
2244
|
})(),
|
|
1961
2245
|
isLast && lastIntent === "compare" && sources.length >= 2 && !replayMessages && /* @__PURE__ */ jsx7(ComparisonMatrix, { sources, defaultCurrency }),
|
|
1962
|
-
isLast && referencedIds.length > 0 && lastIntent !== "compare" && lastAction?.type !== "
|
|
2246
|
+
isLast && referencedIds.length > 0 && lastIntent !== "compare" && lastAction?.type !== "request_kiku_key" && lastAction?.type !== "awaiting_payment" && lastAction?.type !== "checkout" && lastAction?.type !== "capture" && lastAction?.type !== "delete" && lastAction?.type !== "view_history" && !msg.cartSnapshot && /* @__PURE__ */ jsx7(
|
|
1963
2247
|
SourcesCarousel,
|
|
1964
2248
|
{
|
|
1965
2249
|
sources,
|
|
@@ -2032,44 +2316,71 @@ function ChatModal({
|
|
|
2032
2316
|
] })
|
|
2033
2317
|
] }),
|
|
2034
2318
|
error && /* @__PURE__ */ jsx7("div", { className: "hsk-cb-error", children: getFriendlyError(error) }),
|
|
2035
|
-
!replayMessages &&
|
|
2319
|
+
!replayMessages && keyPhase === "prompt_key" && /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-ai-msg", children: [
|
|
2036
2320
|
/* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx7(SparkleIcon2, {}) }),
|
|
2037
2321
|
/* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-text", children: /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-phone-form", children: [
|
|
2038
|
-
/* @__PURE__ */ jsx7("label", { className: "hsk-cb-phone-label", children: "
|
|
2322
|
+
/* @__PURE__ */ jsx7("label", { className: "hsk-cb-phone-label", children: "Paste your kiku key \u2014 or create one" }),
|
|
2039
2323
|
/* @__PURE__ */ jsx7(
|
|
2040
2324
|
"input",
|
|
2041
2325
|
{
|
|
2042
2326
|
type: "text",
|
|
2043
2327
|
className: "hsk-cb-phone-input",
|
|
2044
|
-
placeholder: "
|
|
2045
|
-
value:
|
|
2046
|
-
onChange: (e) =>
|
|
2047
|
-
onKeyDown: (e) => e.key === "Enter" &&
|
|
2328
|
+
placeholder: "kiku_\u2026",
|
|
2329
|
+
value: keyInput,
|
|
2330
|
+
onChange: (e) => setKeyInput(e.target.value),
|
|
2331
|
+
onKeyDown: (e) => e.key === "Enter" && handleUseExistingKey(),
|
|
2048
2332
|
autoFocus: true
|
|
2049
2333
|
}
|
|
2050
2334
|
),
|
|
2051
|
-
/* @__PURE__ */
|
|
2335
|
+
/* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8 }, children: [
|
|
2336
|
+
/* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", onClick: handleUseExistingKey, disabled: !keyInput.trim(), children: "Use my key" }),
|
|
2337
|
+
/* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", onClick: handleCreateKey, disabled: minting, children: minting ? "Creating\u2026" : "I'm new \u2014 create my key" })
|
|
2338
|
+
] })
|
|
2052
2339
|
] }) }) })
|
|
2053
2340
|
] }),
|
|
2054
|
-
!replayMessages &&
|
|
2341
|
+
!replayMessages && mintedKey && /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-ai-msg", children: [
|
|
2055
2342
|
/* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx7(SparkleIcon2, {}) }),
|
|
2056
2343
|
/* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx7("div", { className: "hsk-cb-ai-text", children: /* @__PURE__ */ jsxs6("div", { style: { padding: "10px 12px", border: "1px solid var(--hsk-border, #e5e5e5)", borderRadius: 8 }, children: [
|
|
2057
|
-
/* @__PURE__ */ jsx7("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 }, children: "\u{1F511} Your
|
|
2058
|
-
/* @__PURE__ */ jsx7("code", { style: { display: "block", fontSize: 14, fontWeight: 700, marginBottom: 6, wordBreak: "break-all" }, children:
|
|
2344
|
+
/* @__PURE__ */ jsx7("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 }, children: "\u{1F511} Your kiku key \u2014 shown only once" }),
|
|
2345
|
+
/* @__PURE__ */ jsx7("code", { style: { display: "block", fontSize: 14, fontWeight: 700, marginBottom: 6, wordBreak: "break-all" }, children: mintedKey }),
|
|
2059
2346
|
/* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
2060
2347
|
/* @__PURE__ */ jsx7("button", { className: "hsk-cb-phone-submit", style: { padding: "4px 10px" }, onClick: () => {
|
|
2061
2348
|
try {
|
|
2062
|
-
navigator.clipboard?.writeText(
|
|
2349
|
+
navigator.clipboard?.writeText(mintedKey);
|
|
2063
2350
|
} catch {
|
|
2064
2351
|
}
|
|
2065
2352
|
}, children: "Copy" }),
|
|
2066
|
-
/* @__PURE__ */
|
|
2353
|
+
/* @__PURE__ */ jsxs6("span", { style: { fontSize: 11, opacity: 0.7 }, children: [
|
|
2354
|
+
"Disappears in ",
|
|
2355
|
+
keyCountdown,
|
|
2356
|
+
"s. Save it like a password \u2014 if lost, the memory it opens is lost with it."
|
|
2357
|
+
] })
|
|
2067
2358
|
] })
|
|
2068
2359
|
] }) }) })
|
|
2069
2360
|
] }),
|
|
2070
2361
|
/* @__PURE__ */ jsx7("div", { ref: bottomRef, style: { height: 1 } })
|
|
2071
2362
|
] }),
|
|
2072
2363
|
!replayMessages && /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-input-wrap", children: [
|
|
2364
|
+
showAtPicker && /* @__PURE__ */ jsx7(
|
|
2365
|
+
AtPickerMenu,
|
|
2366
|
+
{
|
|
2367
|
+
onSelect: handleSelectExtension,
|
|
2368
|
+
onDismiss: () => setShowAtPicker(false)
|
|
2369
|
+
}
|
|
2370
|
+
),
|
|
2371
|
+
showKikuPicker && /* @__PURE__ */ jsx7(
|
|
2372
|
+
KikuPickerMenu,
|
|
2373
|
+
{
|
|
2374
|
+
sources,
|
|
2375
|
+
referencedIds,
|
|
2376
|
+
defaultCurrency,
|
|
2377
|
+
onCapture: handleKikuCapture,
|
|
2378
|
+
onCaptureAll: handleKikuCaptureAll,
|
|
2379
|
+
onViewHistory: handleKikuViewHistory,
|
|
2380
|
+
onDelete: handleKikuDelete,
|
|
2381
|
+
onDismiss: () => setShowKikuPicker(false)
|
|
2382
|
+
}
|
|
2383
|
+
),
|
|
2073
2384
|
attachments.length > 0 && /* @__PURE__ */ jsx7("div", { className: "hsk-cb-img-strip", children: attachments.map((att, i) => /* @__PURE__ */ jsxs6("div", { className: "hsk-cb-img-thumb-wrap", children: [
|
|
2074
2385
|
/* @__PURE__ */ jsx7("img", { src: att.data, alt: `attachment ${i + 1}`, className: "hsk-cb-img-thumb" }),
|
|
2075
2386
|
/* @__PURE__ */ jsx7(
|
|
@@ -2168,6 +2479,7 @@ function KikuButton({
|
|
|
2168
2479
|
theme,
|
|
2169
2480
|
classNames = {},
|
|
2170
2481
|
enableVoice = false,
|
|
2482
|
+
voiceLang,
|
|
2171
2483
|
enableVision = false,
|
|
2172
2484
|
visionCategoryHint
|
|
2173
2485
|
}) {
|
|
@@ -2237,6 +2549,7 @@ function KikuButton({
|
|
|
2237
2549
|
theme,
|
|
2238
2550
|
classNames,
|
|
2239
2551
|
enableVoice,
|
|
2552
|
+
voiceLang,
|
|
2240
2553
|
enableVision,
|
|
2241
2554
|
visionCategoryHint
|
|
2242
2555
|
}
|