@paymanai/payman-ask-sdk 4.0.23 → 4.0.25
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.js +475 -428
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +218 -171
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +199 -63
- package/dist/index.native.js.map +1 -1
- package/dist/styles.css +0 -30
- package/dist/styles.css.map +1 -1
- package/package.json +3 -2
package/dist/index.native.js
CHANGED
|
@@ -55,6 +55,7 @@ var chatStore = {
|
|
|
55
55
|
memoryStore.delete(key);
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
+
var EMPTY_USER_ACTION_STATE = { prompts: [], notifications: [] };
|
|
58
59
|
var streams = /* @__PURE__ */ new Map();
|
|
59
60
|
var activeStreamStore = {
|
|
60
61
|
has(key) {
|
|
@@ -63,7 +64,11 @@ var activeStreamStore = {
|
|
|
63
64
|
get(key) {
|
|
64
65
|
const entry = streams.get(key);
|
|
65
66
|
if (!entry) return null;
|
|
66
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
messages: entry.messages,
|
|
69
|
+
isWaiting: entry.isWaiting,
|
|
70
|
+
userActionState: entry.userActionState
|
|
71
|
+
};
|
|
67
72
|
},
|
|
68
73
|
// Called before startStream — registers the controller and initial messages
|
|
69
74
|
start(key, abortController, initialMessages) {
|
|
@@ -71,6 +76,7 @@ var activeStreamStore = {
|
|
|
71
76
|
streams.set(key, {
|
|
72
77
|
messages: initialMessages,
|
|
73
78
|
isWaiting: true,
|
|
79
|
+
userActionState: EMPTY_USER_ACTION_STATE,
|
|
74
80
|
abortController,
|
|
75
81
|
listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
|
|
76
82
|
});
|
|
@@ -81,20 +87,27 @@ var activeStreamStore = {
|
|
|
81
87
|
if (!entry) return;
|
|
82
88
|
const next = typeof updater === "function" ? updater(entry.messages) : updater;
|
|
83
89
|
entry.messages = next;
|
|
84
|
-
entry.listeners.forEach((l) => l(next, entry.isWaiting));
|
|
90
|
+
entry.listeners.forEach((l) => l(next, entry.isWaiting, entry.userActionState));
|
|
85
91
|
},
|
|
86
92
|
setWaiting(key, waiting) {
|
|
87
93
|
const entry = streams.get(key);
|
|
88
94
|
if (!entry) return;
|
|
89
95
|
entry.isWaiting = waiting;
|
|
90
|
-
entry.listeners.forEach((l) => l(entry.messages, waiting));
|
|
96
|
+
entry.listeners.forEach((l) => l(entry.messages, waiting, entry.userActionState));
|
|
97
|
+
},
|
|
98
|
+
applyUserActionState(key, updater) {
|
|
99
|
+
const entry = streams.get(key);
|
|
100
|
+
if (!entry) return;
|
|
101
|
+
const next = typeof updater === "function" ? updater(entry.userActionState) : updater;
|
|
102
|
+
entry.userActionState = next;
|
|
103
|
+
entry.listeners.forEach((l) => l(entry.messages, entry.isWaiting, next));
|
|
91
104
|
},
|
|
92
105
|
// Called when stream completes — persists to chatStore and cleans up
|
|
93
106
|
complete(key) {
|
|
94
107
|
const entry = streams.get(key);
|
|
95
108
|
if (!entry) return;
|
|
96
109
|
entry.isWaiting = false;
|
|
97
|
-
entry.listeners.forEach((l) => l(entry.messages, false));
|
|
110
|
+
entry.listeners.forEach((l) => l(entry.messages, false, entry.userActionState));
|
|
98
111
|
const toSave = entry.messages.filter((m) => !m.isStreaming);
|
|
99
112
|
if (toSave.length > 0) chatStore.set(key, toSave);
|
|
100
113
|
streams.delete(key);
|
|
@@ -517,9 +530,9 @@ function processStreamEventV2(rawEvent, state) {
|
|
|
517
530
|
state.hasError = true;
|
|
518
531
|
state.errorMessage = "WORKFLOW_FAILED";
|
|
519
532
|
}
|
|
520
|
-
state.steps.forEach((
|
|
521
|
-
if (
|
|
522
|
-
|
|
533
|
+
state.steps.forEach((step2) => {
|
|
534
|
+
if (step2.status === "in_progress") {
|
|
535
|
+
step2.status = "completed";
|
|
523
536
|
}
|
|
524
537
|
});
|
|
525
538
|
state.lastEventType = eventType;
|
|
@@ -934,11 +947,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
934
947
|
errorDetails: isAborted ? void 0 : error.message,
|
|
935
948
|
content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
|
|
936
949
|
currentMessage: isAborted ? "Thinking..." : void 0,
|
|
937
|
-
steps: [...state.steps].map((
|
|
938
|
-
if (
|
|
939
|
-
return { ...
|
|
950
|
+
steps: [...state.steps].map((step2) => {
|
|
951
|
+
if (step2.status === "in_progress" && isAborted) {
|
|
952
|
+
return { ...step2, status: "pending" };
|
|
940
953
|
}
|
|
941
|
-
return
|
|
954
|
+
return step2;
|
|
942
955
|
}),
|
|
943
956
|
currentExecutingStepId: void 0
|
|
944
957
|
} : msg
|
|
@@ -1024,11 +1037,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1024
1037
|
isCancelled: isAborted,
|
|
1025
1038
|
errorDetails: isAborted ? void 0 : error.message,
|
|
1026
1039
|
content: isAborted ? state.finalResponse || "" : state.finalResponse || FRIENDLY_ERROR_MESSAGE,
|
|
1027
|
-
steps: [...state.steps].map((
|
|
1028
|
-
if (
|
|
1029
|
-
return { ...
|
|
1040
|
+
steps: [...state.steps].map((step2) => {
|
|
1041
|
+
if (step2.status === "in_progress" && isAborted) {
|
|
1042
|
+
return { ...step2, status: "pending" };
|
|
1030
1043
|
}
|
|
1031
|
-
return
|
|
1044
|
+
return step2;
|
|
1032
1045
|
}),
|
|
1033
1046
|
currentExecutingStepId: void 0
|
|
1034
1047
|
} : msg
|
|
@@ -1049,11 +1062,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
1049
1062
|
};
|
|
1050
1063
|
}
|
|
1051
1064
|
function createCancelledMessageUpdate(steps, currentMessage) {
|
|
1052
|
-
const updatedSteps = steps.map((
|
|
1053
|
-
if (
|
|
1054
|
-
return { ...
|
|
1065
|
+
const updatedSteps = steps.map((step2) => {
|
|
1066
|
+
if (step2.status === "in_progress") {
|
|
1067
|
+
return { ...step2, status: "pending" };
|
|
1055
1068
|
}
|
|
1056
|
-
return
|
|
1069
|
+
return step2;
|
|
1057
1070
|
});
|
|
1058
1071
|
return {
|
|
1059
1072
|
isStreaming: false,
|
|
@@ -1102,12 +1115,35 @@ async function resendUserAction(config, userActionId) {
|
|
|
1102
1115
|
async function expireUserAction(config, userActionId) {
|
|
1103
1116
|
return sendUserActionRequest(config, userActionId, "expired");
|
|
1104
1117
|
}
|
|
1105
|
-
|
|
1118
|
+
function resolveUserActionExpiresAt(req, existing) {
|
|
1119
|
+
if (typeof req.expirySeconds !== "number" || req.expirySeconds <= 0) {
|
|
1120
|
+
return void 0;
|
|
1121
|
+
}
|
|
1122
|
+
if (existing?.expiresAt && existing.userActionId === req.userActionId) {
|
|
1123
|
+
return existing.expiresAt;
|
|
1124
|
+
}
|
|
1125
|
+
return Date.now() + Math.floor(req.expirySeconds) * 1e3;
|
|
1126
|
+
}
|
|
1127
|
+
function getUserActionSecondsLeft(prompt) {
|
|
1128
|
+
if (typeof prompt.expiresAt === "number" && prompt.expiresAt > 0) {
|
|
1129
|
+
return Math.max(0, Math.ceil((prompt.expiresAt - Date.now()) / 1e3));
|
|
1130
|
+
}
|
|
1131
|
+
if (typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0) {
|
|
1132
|
+
return Math.floor(prompt.expirySeconds);
|
|
1133
|
+
}
|
|
1134
|
+
return void 0;
|
|
1135
|
+
}
|
|
1136
|
+
function isUserActionExpired(prompt) {
|
|
1137
|
+
const left = getUserActionSecondsLeft(prompt);
|
|
1138
|
+
return left !== void 0 && left <= 0;
|
|
1139
|
+
}
|
|
1140
|
+
var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
|
|
1106
1141
|
function upsertPrompt(prompts, req) {
|
|
1107
|
-
const active = { ...req, status: "pending" };
|
|
1108
1142
|
const idx = prompts.findIndex(
|
|
1109
1143
|
(p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
|
|
1110
1144
|
);
|
|
1145
|
+
const expiresAt = resolveUserActionExpiresAt(req, idx >= 0 ? prompts[idx] : void 0);
|
|
1146
|
+
const active = { ...req, status: "pending", expiresAt };
|
|
1111
1147
|
if (idx >= 0) {
|
|
1112
1148
|
const next = prompts.slice();
|
|
1113
1149
|
next[idx] = active;
|
|
@@ -1178,7 +1214,28 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1178
1214
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1179
1215
|
[]
|
|
1180
1216
|
);
|
|
1181
|
-
const [userActionState, setUserActionState] = react.useState(
|
|
1217
|
+
const [userActionState, setUserActionState] = react.useState(() => {
|
|
1218
|
+
if (!config.userId) return EMPTY_USER_ACTION_STATE2;
|
|
1219
|
+
return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
|
|
1220
|
+
});
|
|
1221
|
+
const storeAwareSetUserActionState = react.useCallback(
|
|
1222
|
+
(updater) => {
|
|
1223
|
+
const streamUserId = streamUserIdRef.current;
|
|
1224
|
+
const currentUserId = configRef.current.userId;
|
|
1225
|
+
const storeKey = streamUserId ?? currentUserId;
|
|
1226
|
+
if (storeKey && activeStreamStore.has(storeKey)) {
|
|
1227
|
+
activeStreamStore.applyUserActionState(
|
|
1228
|
+
storeKey,
|
|
1229
|
+
updater
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
if (!streamUserId || streamUserId === currentUserId) {
|
|
1233
|
+
setUserActionState(updater);
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1237
|
+
[]
|
|
1238
|
+
);
|
|
1182
1239
|
const wrappedCallbacks = react.useMemo(() => ({
|
|
1183
1240
|
...callbacksRef.current,
|
|
1184
1241
|
onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
|
|
@@ -1188,14 +1245,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1188
1245
|
onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
|
|
1189
1246
|
onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
|
|
1190
1247
|
onUserActionRequired: (request) => {
|
|
1191
|
-
|
|
1248
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1192
1249
|
...prev,
|
|
1193
1250
|
prompts: upsertPrompt(prev.prompts, request)
|
|
1194
1251
|
}));
|
|
1195
1252
|
callbacksRef.current.onUserActionRequired?.(request);
|
|
1196
1253
|
},
|
|
1197
1254
|
onUserNotification: (notification) => {
|
|
1198
|
-
|
|
1255
|
+
storeAwareSetUserActionState(
|
|
1199
1256
|
(prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
|
|
1200
1257
|
);
|
|
1201
1258
|
callbacksRef.current.onUserNotification?.(notification);
|
|
@@ -1285,7 +1342,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1285
1342
|
streamUserIdRef.current = void 0;
|
|
1286
1343
|
cancelStreamManager();
|
|
1287
1344
|
setIsWaitingForResponse(false);
|
|
1288
|
-
|
|
1345
|
+
storeAwareSetUserActionState((prev) => ({ ...prev, prompts: [] }));
|
|
1289
1346
|
setMessages(
|
|
1290
1347
|
(prev) => prev.map((msg) => {
|
|
1291
1348
|
if (msg.isStreaming) {
|
|
@@ -1300,7 +1357,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1300
1357
|
return msg;
|
|
1301
1358
|
})
|
|
1302
1359
|
);
|
|
1303
|
-
}, [cancelStreamManager]);
|
|
1360
|
+
}, [cancelStreamManager, storeAwareSetUserActionState]);
|
|
1304
1361
|
const resetSession = react.useCallback(() => {
|
|
1305
1362
|
const streamUserId = streamUserIdRef.current ?? configRef.current.userId;
|
|
1306
1363
|
if (streamUserId) {
|
|
@@ -1314,7 +1371,7 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1314
1371
|
sessionIdRef.current = void 0;
|
|
1315
1372
|
abortControllerRef.current?.abort();
|
|
1316
1373
|
setIsWaitingForResponse(false);
|
|
1317
|
-
|
|
1374
|
+
storeAwareSetUserActionState(EMPTY_USER_ACTION_STATE2);
|
|
1318
1375
|
}, []);
|
|
1319
1376
|
const getSessionId = react.useCallback(() => {
|
|
1320
1377
|
return sessionIdRef.current;
|
|
@@ -1324,21 +1381,21 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1324
1381
|
}, [messages]);
|
|
1325
1382
|
const setPromptStatus = react.useCallback(
|
|
1326
1383
|
(userActionId, status) => {
|
|
1327
|
-
|
|
1384
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1328
1385
|
...prev,
|
|
1329
1386
|
prompts: prev.prompts.map(
|
|
1330
1387
|
(p) => p.userActionId === userActionId ? { ...p, status } : p
|
|
1331
1388
|
)
|
|
1332
1389
|
}));
|
|
1333
1390
|
},
|
|
1334
|
-
[]
|
|
1391
|
+
[storeAwareSetUserActionState]
|
|
1335
1392
|
);
|
|
1336
1393
|
const removePrompt = react.useCallback((userActionId) => {
|
|
1337
|
-
|
|
1394
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1338
1395
|
...prev,
|
|
1339
1396
|
prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
|
|
1340
1397
|
}));
|
|
1341
|
-
}, []);
|
|
1398
|
+
}, [storeAwareSetUserActionState]);
|
|
1342
1399
|
const submitUserAction2 = react.useCallback(
|
|
1343
1400
|
async (userActionId, content) => {
|
|
1344
1401
|
setPromptStatus(userActionId, "submitting");
|
|
@@ -1407,11 +1464,11 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1407
1464
|
[setPromptStatus]
|
|
1408
1465
|
);
|
|
1409
1466
|
const dismissNotification = react.useCallback((id) => {
|
|
1410
|
-
|
|
1467
|
+
storeAwareSetUserActionState((prev) => ({
|
|
1411
1468
|
...prev,
|
|
1412
1469
|
notifications: prev.notifications.filter((n) => n.id !== id)
|
|
1413
1470
|
}));
|
|
1414
|
-
}, []);
|
|
1471
|
+
}, [storeAwareSetUserActionState]);
|
|
1415
1472
|
react.useEffect(() => {
|
|
1416
1473
|
const prevSubscriptionUserId = subscriptionPrevUserIdRef.current;
|
|
1417
1474
|
subscriptionPrevUserIdRef.current = config.userId;
|
|
@@ -1420,14 +1477,17 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1420
1477
|
if (prevSubscriptionUserId && prevSubscriptionUserId !== userId && streamUserIdRef.current === prevSubscriptionUserId && !activeStreamStore.has(prevSubscriptionUserId) && activeStreamStore.has(userId)) {
|
|
1421
1478
|
streamUserIdRef.current = userId;
|
|
1422
1479
|
}
|
|
1423
|
-
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
|
|
1480
|
+
const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting, actions) => {
|
|
1424
1481
|
setMessages(msgs);
|
|
1425
1482
|
setIsWaitingForResponse(isWaiting);
|
|
1483
|
+
setUserActionState(actions);
|
|
1426
1484
|
});
|
|
1427
1485
|
const active = activeStreamStore.get(userId);
|
|
1428
1486
|
if (active) {
|
|
1487
|
+
streamUserIdRef.current = userId;
|
|
1429
1488
|
setMessages(active.messages);
|
|
1430
1489
|
setIsWaitingForResponse(active.isWaiting);
|
|
1490
|
+
setUserActionState(active.userActionState);
|
|
1431
1491
|
}
|
|
1432
1492
|
return unsubscribe;
|
|
1433
1493
|
}, [config.userId]);
|
|
@@ -1455,12 +1515,14 @@ function useChatV2(config, callbacks = {}) {
|
|
|
1455
1515
|
setMessages([]);
|
|
1456
1516
|
sessionIdRef.current = void 0;
|
|
1457
1517
|
setIsWaitingForResponse(false);
|
|
1458
|
-
setUserActionState(
|
|
1518
|
+
setUserActionState(EMPTY_USER_ACTION_STATE2);
|
|
1459
1519
|
} else if (config.userId) {
|
|
1460
1520
|
const active = activeStreamStore.get(config.userId);
|
|
1461
1521
|
if (active) {
|
|
1522
|
+
streamUserIdRef.current = config.userId;
|
|
1462
1523
|
setMessages(active.messages);
|
|
1463
1524
|
setIsWaitingForResponse(active.isWaiting);
|
|
1525
|
+
setUserActionState(active.userActionState);
|
|
1464
1526
|
sessionIdRef.current = getSessionIdFromMessages(active.messages) ?? config.initialSessionId;
|
|
1465
1527
|
return;
|
|
1466
1528
|
}
|
|
@@ -1828,6 +1890,85 @@ function usePaymanChat() {
|
|
|
1828
1890
|
}
|
|
1829
1891
|
return context;
|
|
1830
1892
|
}
|
|
1893
|
+
|
|
1894
|
+
// src/utils/markdownImageToken.ts
|
|
1895
|
+
function step(text, i) {
|
|
1896
|
+
if (text[i] === "\\" && i + 1 < text.length) return i + 2;
|
|
1897
|
+
return i + 1;
|
|
1898
|
+
}
|
|
1899
|
+
function scanAltText(text, start) {
|
|
1900
|
+
let i = start;
|
|
1901
|
+
while (i < text.length) {
|
|
1902
|
+
if (text[i] === "]") return i;
|
|
1903
|
+
i = step(text, i);
|
|
1904
|
+
}
|
|
1905
|
+
return -1;
|
|
1906
|
+
}
|
|
1907
|
+
function scanOptionalTitle(text, start) {
|
|
1908
|
+
let i = start;
|
|
1909
|
+
while (i < text.length && /[\t\n ]/.test(text[i])) i++;
|
|
1910
|
+
if (i >= text.length) return i;
|
|
1911
|
+
const quote = text[i];
|
|
1912
|
+
if (quote !== '"' && quote !== "'") return i;
|
|
1913
|
+
i++;
|
|
1914
|
+
while (i < text.length) {
|
|
1915
|
+
if (text[i] === quote) return i + 1;
|
|
1916
|
+
i = step(text, i);
|
|
1917
|
+
}
|
|
1918
|
+
return -1;
|
|
1919
|
+
}
|
|
1920
|
+
function scanAngleBracketDestination(text, start) {
|
|
1921
|
+
let i = start + 1;
|
|
1922
|
+
while (i < text.length) {
|
|
1923
|
+
if (text[i] === ">") return i + 1;
|
|
1924
|
+
i = step(text, i);
|
|
1925
|
+
}
|
|
1926
|
+
return -1;
|
|
1927
|
+
}
|
|
1928
|
+
function scanRawDestination(text, start) {
|
|
1929
|
+
let i = start;
|
|
1930
|
+
let depth = 1;
|
|
1931
|
+
while (i < text.length) {
|
|
1932
|
+
const char = text[i];
|
|
1933
|
+
if (char === "\\") {
|
|
1934
|
+
if (i + 1 >= text.length) return -1;
|
|
1935
|
+
i += 2;
|
|
1936
|
+
continue;
|
|
1937
|
+
}
|
|
1938
|
+
if (char === "(") depth++;
|
|
1939
|
+
else if (char === ")") {
|
|
1940
|
+
depth--;
|
|
1941
|
+
if (depth === 0) return i + 1;
|
|
1942
|
+
}
|
|
1943
|
+
i++;
|
|
1944
|
+
}
|
|
1945
|
+
return -1;
|
|
1946
|
+
}
|
|
1947
|
+
function completeMarkdownImageTokenLength(text) {
|
|
1948
|
+
if (!text.startsWith("![")) return 0;
|
|
1949
|
+
const altEnd = scanAltText(text, 2);
|
|
1950
|
+
if (altEnd === -1 || altEnd + 1 >= text.length || text[altEnd + 1] !== "(") {
|
|
1951
|
+
return 0;
|
|
1952
|
+
}
|
|
1953
|
+
let i = altEnd + 2;
|
|
1954
|
+
if (i >= text.length) return 0;
|
|
1955
|
+
if (text[i] === "<") {
|
|
1956
|
+
i = scanAngleBracketDestination(text, i);
|
|
1957
|
+
if (i === -1) return 0;
|
|
1958
|
+
i = scanOptionalTitle(text, i);
|
|
1959
|
+
if (i === -1 || i >= text.length || text[i] !== ")") return 0;
|
|
1960
|
+
return i + 1;
|
|
1961
|
+
}
|
|
1962
|
+
i = scanRawDestination(text, i);
|
|
1963
|
+
return i === -1 ? 0 : i;
|
|
1964
|
+
}
|
|
1965
|
+
function stripIncompleteImageToken(text) {
|
|
1966
|
+
const lastBang = text.lastIndexOf("![");
|
|
1967
|
+
if (lastBang === -1) return text;
|
|
1968
|
+
const suffix = text.slice(lastBang);
|
|
1969
|
+
if (completeMarkdownImageTokenLength(suffix) > 0) return text;
|
|
1970
|
+
return text.slice(0, lastBang);
|
|
1971
|
+
}
|
|
1831
1972
|
if (reactNative.Platform.OS === "android" && reactNative.UIManager.setLayoutAnimationEnabledExperimental) {
|
|
1832
1973
|
reactNative.UIManager.setLayoutAnimationEnabledExperimental(true);
|
|
1833
1974
|
}
|
|
@@ -2280,13 +2421,6 @@ function UserBubble({ message, accent }) {
|
|
|
2280
2421
|
if (!text) return null;
|
|
2281
2422
|
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [sd.bubbleRow, sd.rowUser], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [sd.bubble, sd.bubbleUser, { backgroundColor: accent }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [sd.bubbleText, sd.textUser], children: text }) }) });
|
|
2282
2423
|
}
|
|
2283
|
-
function stripIncompleteImageToken(text) {
|
|
2284
|
-
const lastBang = text.lastIndexOf("![");
|
|
2285
|
-
if (lastBang === -1) return text;
|
|
2286
|
-
const after = text.slice(lastBang);
|
|
2287
|
-
if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
|
|
2288
|
-
return text.slice(0, lastBang);
|
|
2289
|
-
}
|
|
2290
2424
|
function stripDuplicatePromptText(text, promptText) {
|
|
2291
2425
|
if (!promptText) return text;
|
|
2292
2426
|
const pm = promptText.replace(/\\n/g, "\n").trim();
|
|
@@ -2376,35 +2510,37 @@ function uaPalette(isDark) {
|
|
|
2376
2510
|
grabber: "rgba(15,23,42,0.18)"
|
|
2377
2511
|
};
|
|
2378
2512
|
}
|
|
2379
|
-
function promptInitialSeconds(prompt) {
|
|
2380
|
-
return prompt && typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Math.floor(prompt.expirySeconds) : void 0;
|
|
2381
|
-
}
|
|
2382
2513
|
function promptTimerKey(prompt) {
|
|
2383
|
-
return prompt ? `${prompt.userActionId}|${prompt.subAction ?? ""}` : "";
|
|
2514
|
+
return prompt ? `${prompt.userActionId}|${prompt.subAction ?? ""}|${prompt.expiresAt ?? ""}` : "";
|
|
2384
2515
|
}
|
|
2385
2516
|
function useExpiredFlag(prompt) {
|
|
2386
|
-
const initial = promptInitialSeconds(prompt);
|
|
2387
2517
|
const key = promptTimerKey(prompt);
|
|
2388
|
-
const [expired, setExpired] = react.useState(false);
|
|
2518
|
+
const [expired, setExpired] = react.useState(() => prompt ? isUserActionExpired(prompt) : false);
|
|
2389
2519
|
react.useEffect(() => {
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2520
|
+
const secondsLeft = prompt ? getUserActionSecondsLeft(prompt) : void 0;
|
|
2521
|
+
setExpired(secondsLeft !== void 0 && secondsLeft <= 0);
|
|
2522
|
+
if (secondsLeft === void 0 || secondsLeft <= 0) return;
|
|
2523
|
+
const t = setTimeout(() => setExpired(true), secondsLeft * 1e3);
|
|
2393
2524
|
return () => clearTimeout(t);
|
|
2394
|
-
}, [key,
|
|
2525
|
+
}, [key, prompt]);
|
|
2395
2526
|
return expired;
|
|
2396
2527
|
}
|
|
2397
|
-
function useSecondsLeft(
|
|
2398
|
-
const
|
|
2399
|
-
const [left, setLeft] = react.useState(
|
|
2528
|
+
function useSecondsLeft(prompt, restartKey) {
|
|
2529
|
+
const deadlineMs = prompt && typeof prompt.expiresAt === "number" && prompt.expiresAt > 0 ? prompt.expiresAt : prompt && typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Date.now() + Math.floor(prompt.expirySeconds) * 1e3 : void 0;
|
|
2530
|
+
const [left, setLeft] = react.useState(
|
|
2531
|
+
() => deadlineMs !== void 0 ? Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3)) : void 0
|
|
2532
|
+
);
|
|
2400
2533
|
react.useEffect(() => {
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2534
|
+
if (deadlineMs === void 0) {
|
|
2535
|
+
setLeft(void 0);
|
|
2536
|
+
return;
|
|
2537
|
+
}
|
|
2538
|
+
const remaining = () => Math.max(0, Math.ceil((deadlineMs - Date.now()) / 1e3));
|
|
2539
|
+
const sync = () => setLeft(remaining());
|
|
2540
|
+
sync();
|
|
2541
|
+
const id = setInterval(sync, 1e3);
|
|
2406
2542
|
return () => clearInterval(id);
|
|
2407
|
-
}, [restartKey,
|
|
2543
|
+
}, [restartKey, deadlineMs]);
|
|
2408
2544
|
return left;
|
|
2409
2545
|
}
|
|
2410
2546
|
function SheetTimerPill({
|
|
@@ -2412,7 +2548,7 @@ function SheetTimerPill({
|
|
|
2412
2548
|
accent,
|
|
2413
2549
|
isDark
|
|
2414
2550
|
}) {
|
|
2415
|
-
const left = useSecondsLeft(prompt
|
|
2551
|
+
const left = useSecondsLeft(prompt, promptTimerKey(prompt));
|
|
2416
2552
|
if (left === void 0 || prompt.status === "stale") return null;
|
|
2417
2553
|
const expired = left <= 0;
|
|
2418
2554
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2434,7 +2570,7 @@ function CardTimerPill({
|
|
|
2434
2570
|
accent,
|
|
2435
2571
|
isDark
|
|
2436
2572
|
}) {
|
|
2437
|
-
const left = useSecondsLeft(prompt
|
|
2573
|
+
const left = useSecondsLeft(prompt, promptTimerKey(prompt));
|
|
2438
2574
|
if (left === void 0 || left <= 0) return null;
|
|
2439
2575
|
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [rc.timer, { backgroundColor: accent + (isDark ? "26" : "14") }], children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: { color: accent, fontSize: 12, fontWeight: "700" }, children: [
|
|
2440
2576
|
left,
|