@agent-native/core 0.84.28 → 0.84.30

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.
Files changed (39) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +13 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/chat/tool-call-display.tsx +50 -8
  5. package/corpus/core/src/client/sse-event-processor.ts +17 -4
  6. package/corpus/core/src/deploy/build.ts +5 -0
  7. package/corpus/core/src/server/analytics.ts +29 -7
  8. package/corpus/core/src/server/ssr-handler.ts +305 -25
  9. package/corpus/core/src/vite/client.ts +3 -0
  10. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +324 -24
  11. package/corpus/templates/analytics/app/i18n/zh-TW.ts +11 -0
  12. package/corpus/templates/analytics/app/i18n-data.ts +110 -0
  13. package/corpus/templates/design/app/pages/DesignEditor.tsx +45 -2
  14. package/corpus/templates/design/changelog/2026-07-01-design-previews-refresh-immediately-after-agent-screen-edits.md +6 -0
  15. package/dist/client/chat/tool-call-display.d.ts +1 -0
  16. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  17. package/dist/client/chat/tool-call-display.js +22 -5
  18. package/dist/client/chat/tool-call-display.js.map +1 -1
  19. package/dist/client/sse-event-processor.d.ts.map +1 -1
  20. package/dist/client/sse-event-processor.js +13 -4
  21. package/dist/client/sse-event-processor.js.map +1 -1
  22. package/dist/collab/routes.d.ts +1 -1
  23. package/dist/deploy/build.js +3 -0
  24. package/dist/deploy/build.js.map +1 -1
  25. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  26. package/dist/notifications/routes.d.ts +2 -2
  27. package/dist/observability/routes.d.ts +8 -8
  28. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  29. package/dist/server/analytics.d.ts +10 -2
  30. package/dist/server/analytics.d.ts.map +1 -1
  31. package/dist/server/analytics.js +26 -5
  32. package/dist/server/analytics.js.map +1 -1
  33. package/dist/server/ssr-handler.d.ts.map +1 -1
  34. package/dist/server/ssr-handler.js +206 -21
  35. package/dist/server/ssr-handler.js.map +1 -1
  36. package/dist/vite/client.d.ts.map +1 -1
  37. package/dist/vite/client.js +1 -0
  38. package/dist/vite/client.js.map +1 -1
  39. package/package.json +1 -1
@@ -14,6 +14,8 @@ import {
14
14
  IconReportAnalytics,
15
15
  IconSearch,
16
16
  IconArchive,
17
+ IconPin,
18
+ IconPlus,
17
19
  IconBuilding,
18
20
  IconLock,
19
21
  IconLink,
@@ -38,11 +40,13 @@ import {
38
40
  useRef,
39
41
  useMemo,
40
42
  Fragment,
43
+ type FormEvent,
41
44
  } from "react";
42
45
  import { Link, useLocation, useNavigate } from "react-router";
43
46
  import { toast } from "sonner";
44
47
 
45
48
  import { getIdToken } from "@/lib/auth";
49
+ import { ANALYTICS_CHAT_STORAGE_KEY } from "@/lib/chat-handoff";
46
50
  import { cn, shortcutModifierLabel } from "@/lib/utils";
47
51
  import {
48
52
  dashboards,
@@ -70,9 +74,11 @@ import {
70
74
  callAction,
71
75
  appPath,
72
76
  navigateWithAgentChatViewTransition,
77
+ useChatThreads,
73
78
  useActionMutation,
74
79
  useChangeVersions,
75
80
  useT,
81
+ type ChatThreadSummary,
76
82
  } from "@agent-native/core/client";
77
83
  import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
78
84
  import { OrgSwitcher } from "@agent-native/core/client/org";
@@ -94,6 +100,7 @@ import {
94
100
  DropdownMenuSeparator,
95
101
  DropdownMenuTrigger,
96
102
  } from "@/components/ui/dropdown-menu";
103
+ import { Input } from "@/components/ui/input";
97
104
  import {
98
105
  Popover,
99
106
  PopoverTrigger,
@@ -1175,6 +1182,296 @@ async function fetchAnalysisDetailForPrefetch(id: string): Promise<unknown> {
1175
1182
  }
1176
1183
  }
1177
1184
 
1185
+ const ANALYTICS_ACTIVE_THREAD_KEY = `agent-chat-active-thread:${ANALYTICS_CHAT_STORAGE_KEY}`;
1186
+
1187
+ function formatThreadAge(updatedAt: number) {
1188
+ const diffMs = Math.max(0, Date.now() - updatedAt);
1189
+ const minutes = Math.floor(diffMs / 60_000);
1190
+ if (minutes < 1) return "now";
1191
+ if (minutes < 60) return `${minutes}m`;
1192
+ const hours = Math.floor(minutes / 60);
1193
+ if (hours < 24) return `${hours}h`;
1194
+ const days = Math.floor(hours / 24);
1195
+ if (days < 7) return `${days}d`;
1196
+ return new Date(updatedAt).toLocaleDateString([], {
1197
+ month: "short",
1198
+ day: "numeric",
1199
+ });
1200
+ }
1201
+
1202
+ function threadTitle(thread: ChatThreadSummary, untitledLabel: string) {
1203
+ return thread.title || thread.preview || untitledLabel;
1204
+ }
1205
+
1206
+ function threadUpdatedAt(thread: ChatThreadSummary) {
1207
+ return Number.isFinite(thread.updatedAt)
1208
+ ? thread.updatedAt
1209
+ : Number.isFinite(thread.createdAt)
1210
+ ? thread.createdAt
1211
+ : 0;
1212
+ }
1213
+
1214
+ function compareThreads(a: ChatThreadSummary, b: ChatThreadSummary) {
1215
+ const aPinned = a.pinnedAt ?? 0;
1216
+ const bPinned = b.pinnedAt ?? 0;
1217
+ if (aPinned || bPinned) return bPinned - aPinned;
1218
+ return threadUpdatedAt(b) - threadUpdatedAt(a);
1219
+ }
1220
+
1221
+ function persistedAnalyticsThreadId() {
1222
+ if (typeof window === "undefined") return null;
1223
+ try {
1224
+ return window.localStorage.getItem(ANALYTICS_ACTIVE_THREAD_KEY);
1225
+ } catch {
1226
+ return null;
1227
+ }
1228
+ }
1229
+
1230
+ function AnalyticsChatsSection() {
1231
+ const navigate = useNavigate();
1232
+ const t = useT();
1233
+ const {
1234
+ threads,
1235
+ activeThreadId,
1236
+ createThread,
1237
+ switchThread,
1238
+ pinThread,
1239
+ archiveThread,
1240
+ renameThread,
1241
+ refreshThreads,
1242
+ } = useChatThreads(undefined, ANALYTICS_CHAT_STORAGE_KEY, undefined, {
1243
+ autoCreate: false,
1244
+ restoreActiveThread: false,
1245
+ });
1246
+ const [renamingThreadId, setRenamingThreadId] = useState<string | null>(null);
1247
+ const [renameDraft, setRenameDraft] = useState("");
1248
+ const renameInputRef = useRef<HTMLInputElement | null>(null);
1249
+ const committingRenameRef = useRef(false);
1250
+
1251
+ const visibleThreads = useMemo(
1252
+ () =>
1253
+ threads
1254
+ .filter((thread) => thread.messageCount > 0 && !thread.archivedAt)
1255
+ .sort(compareThreads)
1256
+ .slice(0, SIDEBAR_PREVIEW_COUNT),
1257
+ [threads],
1258
+ );
1259
+
1260
+ useEffect(() => {
1261
+ const refresh = () => refreshThreads();
1262
+ const handleRunning = (event: Event) => {
1263
+ const detail = (event as CustomEvent).detail as
1264
+ | { isRunning?: unknown }
1265
+ | undefined;
1266
+ if (typeof detail?.isRunning === "boolean") refreshThreads();
1267
+ };
1268
+
1269
+ window.addEventListener("agent-chat:threads-updated", refresh);
1270
+ window.addEventListener("agentNative.chatRunning", handleRunning);
1271
+ window.addEventListener("focus", refresh);
1272
+ return () => {
1273
+ window.removeEventListener("agent-chat:threads-updated", refresh);
1274
+ window.removeEventListener("agentNative.chatRunning", handleRunning);
1275
+ window.removeEventListener("focus", refresh);
1276
+ };
1277
+ }, [refreshThreads]);
1278
+
1279
+ useEffect(() => {
1280
+ if (!renamingThreadId) return;
1281
+ requestAnimationFrame(() => {
1282
+ renameInputRef.current?.focus();
1283
+ renameInputRef.current?.select();
1284
+ });
1285
+ }, [renamingThreadId]);
1286
+
1287
+ function openThread(threadId: string, options?: { isNew?: boolean }) {
1288
+ switchThread(threadId);
1289
+ navigateWithAgentChatViewTransition(navigate, "/ask");
1290
+ window.requestAnimationFrame(() => {
1291
+ window.dispatchEvent(
1292
+ new CustomEvent("agent-chat:open-thread", {
1293
+ detail: { threadId, newThread: options?.isNew === true },
1294
+ }),
1295
+ );
1296
+ });
1297
+ }
1298
+
1299
+ async function handleNewChat() {
1300
+ const threadId = await createThread();
1301
+ if (threadId) openThread(threadId, { isNew: true });
1302
+ }
1303
+
1304
+ async function handleArchiveThread(threadId: string) {
1305
+ const wasActive =
1306
+ threadId === activeThreadId || threadId === persistedAnalyticsThreadId();
1307
+ const archived = await archiveThread(threadId);
1308
+ if (!archived) {
1309
+ toast.error(t("chat.archiveFailed"));
1310
+ return;
1311
+ }
1312
+ if (wasActive) {
1313
+ await handleNewChat();
1314
+ }
1315
+ }
1316
+
1317
+ function startRenameThread(thread: ChatThreadSummary) {
1318
+ committingRenameRef.current = false;
1319
+ setRenameDraft(threadTitle(thread, t("chat.untitledChat")));
1320
+ setRenamingThreadId(thread.id);
1321
+ }
1322
+
1323
+ function cancelRenameThread() {
1324
+ committingRenameRef.current = true;
1325
+ setRenamingThreadId(null);
1326
+ setRenameDraft("");
1327
+ }
1328
+
1329
+ async function commitRenameThread() {
1330
+ if (committingRenameRef.current) return;
1331
+ const threadId = renamingThreadId;
1332
+ const title = renameDraft.trim();
1333
+ if (!threadId) return;
1334
+ committingRenameRef.current = true;
1335
+ setRenamingThreadId(null);
1336
+ setRenameDraft("");
1337
+ if (title) {
1338
+ const renamed = await renameThread(threadId, title);
1339
+ if (!renamed) toast.error(t("chat.renameFailed"));
1340
+ }
1341
+ committingRenameRef.current = false;
1342
+ }
1343
+
1344
+ function handleRenameSubmit(event: FormEvent<HTMLFormElement>) {
1345
+ event.preventDefault();
1346
+ void commitRenameThread();
1347
+ }
1348
+
1349
+ return (
1350
+ <div className="ms-4 min-w-0 space-y-0.5">
1351
+ <p className="min-w-0 truncate px-3 pb-0.5 pt-1 text-[11px] font-medium text-muted-foreground/70">
1352
+ {t("chat.recentChats")}
1353
+ </p>
1354
+ {visibleThreads.map((thread) => {
1355
+ const title = threadTitle(thread, t("chat.untitledChat"));
1356
+ const isActive =
1357
+ thread.id === activeThreadId ||
1358
+ thread.id === persistedAnalyticsThreadId();
1359
+ const isRenaming = thread.id === renamingThreadId;
1360
+ return (
1361
+ <div
1362
+ key={thread.id}
1363
+ className={cn(
1364
+ "group/item relative flex min-w-0 items-center rounded-lg transition-colors",
1365
+ isActive
1366
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
1367
+ : "text-muted-foreground hover:bg-sidebar-accent/50 hover:text-primary",
1368
+ )}
1369
+ >
1370
+ {isRenaming ? (
1371
+ <form
1372
+ onSubmit={handleRenameSubmit}
1373
+ className="flex min-w-0 flex-1 items-center px-1"
1374
+ >
1375
+ <Input
1376
+ ref={renameInputRef}
1377
+ value={renameDraft}
1378
+ onChange={(event) => setRenameDraft(event.target.value)}
1379
+ onBlur={() => void commitRenameThread()}
1380
+ onKeyDown={(event) => {
1381
+ if (event.key === "Escape") {
1382
+ event.preventDefault();
1383
+ cancelRenameThread();
1384
+ }
1385
+ }}
1386
+ maxLength={160}
1387
+ aria-label={t("chat.renameThread", { title })}
1388
+ className="h-6 min-w-0 rounded-sm border-sidebar-border bg-background px-1.5 text-xs"
1389
+ />
1390
+ </form>
1391
+ ) : (
1392
+ <>
1393
+ <Tooltip>
1394
+ <TooltipTrigger asChild>
1395
+ <button
1396
+ type="button"
1397
+ onClick={() => openThread(thread.id)}
1398
+ className="min-w-0 flex-1 px-2 py-1.5 pe-12 text-start text-xs outline-none transition-[padding] focus-visible:ring-2 focus-visible:ring-ring md:pe-2 md:group-hover/item:pe-12 md:group-focus-within/item:pe-12"
1399
+ >
1400
+ <span className="block truncate">{title}</span>
1401
+ </button>
1402
+ </TooltipTrigger>
1403
+ <TooltipContent side="right">{title}</TooltipContent>
1404
+ </Tooltip>
1405
+ <div className="pointer-events-none absolute end-1 top-1/2 flex -translate-y-1/2 items-center gap-0.5">
1406
+ <span className="pointer-events-none pe-1 text-[11px] text-muted-foreground/60 transition-opacity group-hover/item:opacity-0 group-focus-within/item:opacity-0">
1407
+ {isActive ? "" : formatThreadAge(threadUpdatedAt(thread))}
1408
+ </span>
1409
+ <DropdownMenu>
1410
+ <Tooltip>
1411
+ <TooltipTrigger asChild>
1412
+ <DropdownMenuTrigger asChild>
1413
+ <button
1414
+ type="button"
1415
+ aria-label={t("chat.optionsFor", { title })}
1416
+ className="pointer-events-auto rounded p-0.5 text-muted-foreground/50 opacity-0 transition-all hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover/item:opacity-100 group-focus-within/item:opacity-100 data-[state=open]:opacity-100 data-[state=open]:text-foreground"
1417
+ >
1418
+ <IconDots className="h-3 w-3" />
1419
+ </button>
1420
+ </DropdownMenuTrigger>
1421
+ </TooltipTrigger>
1422
+ <TooltipContent side="right">
1423
+ {t("chat.optionsFor", { title })}
1424
+ </TooltipContent>
1425
+ </Tooltip>
1426
+ <DropdownMenuContent
1427
+ side="right"
1428
+ align="start"
1429
+ className="w-44"
1430
+ >
1431
+ <DropdownMenuItem
1432
+ onSelect={() => startRenameThread(thread)}
1433
+ >
1434
+ <IconPencil className="me-2 h-3.5 w-3.5" />
1435
+ {t("chat.renameChat")}
1436
+ </DropdownMenuItem>
1437
+ <DropdownMenuItem
1438
+ onSelect={() =>
1439
+ void pinThread(thread.id, !thread.pinnedAt)
1440
+ }
1441
+ >
1442
+ <IconPin className="me-2 h-3.5 w-3.5" />
1443
+ {thread.pinnedAt
1444
+ ? t("chat.unpinChat")
1445
+ : t("chat.pinChat")}
1446
+ </DropdownMenuItem>
1447
+ <DropdownMenuSeparator />
1448
+ <DropdownMenuItem
1449
+ onSelect={() => void handleArchiveThread(thread.id)}
1450
+ className="text-destructive focus:text-destructive"
1451
+ >
1452
+ <IconArchive className="me-2 h-3.5 w-3.5" />
1453
+ {t("chat.archiveChat")}
1454
+ </DropdownMenuItem>
1455
+ </DropdownMenuContent>
1456
+ </DropdownMenu>
1457
+ </div>
1458
+ </>
1459
+ )}
1460
+ </div>
1461
+ );
1462
+ })}
1463
+ <button
1464
+ type="button"
1465
+ onClick={() => void handleNewChat()}
1466
+ className="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-1.5 text-xs text-muted-foreground/60 hover:bg-sidebar-accent/50 hover:text-primary"
1467
+ >
1468
+ <IconPlus className="h-3 w-3" />
1469
+ {t("chat.newChat")}
1470
+ </button>
1471
+ </div>
1472
+ );
1473
+ }
1474
+
1178
1475
  function getQuerySnapshots<T>(queryClient: QueryClient, queryKey: QueryKey) {
1179
1476
  return queryClient.getQueriesData<T>({ queryKey });
1180
1477
  }
@@ -2102,30 +2399,33 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2102
2399
  <div className="flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden py-2">
2103
2400
  <nav className="grid min-w-0 items-start px-2 text-sm font-medium lg:px-4 space-y-1">
2104
2401
  {/* Ask link */}
2105
- <Link
2106
- to="/ask"
2107
- onClick={(event) => {
2108
- if (
2109
- location.pathname !== "/ask" &&
2110
- !event.metaKey &&
2111
- !event.ctrlKey &&
2112
- !event.shiftKey &&
2113
- !event.altKey
2114
- ) {
2115
- event.preventDefault();
2116
- navigateWithAgentChatViewTransition(navigate, "/ask");
2117
- }
2118
- }}
2119
- className={cn(
2120
- "flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:text-primary",
2121
- location.pathname === "/ask"
2122
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
2123
- : "text-muted-foreground hover:bg-sidebar-accent/50",
2124
- )}
2125
- >
2126
- <IconMessageCircle className="h-4 w-4" />
2127
- {t("navigation.ask")}
2128
- </Link>
2402
+ <div className="min-w-0 space-y-1">
2403
+ <Link
2404
+ to="/ask"
2405
+ onClick={(event) => {
2406
+ if (
2407
+ location.pathname !== "/ask" &&
2408
+ !event.metaKey &&
2409
+ !event.ctrlKey &&
2410
+ !event.shiftKey &&
2411
+ !event.altKey
2412
+ ) {
2413
+ event.preventDefault();
2414
+ navigateWithAgentChatViewTransition(navigate, "/ask");
2415
+ }
2416
+ }}
2417
+ className={cn(
2418
+ "flex items-center gap-3 rounded-lg px-3 py-2 transition-all hover:text-primary",
2419
+ location.pathname === "/ask"
2420
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
2421
+ : "text-muted-foreground hover:bg-sidebar-accent/50",
2422
+ )}
2423
+ >
2424
+ <IconMessageCircle className="h-4 w-4" />
2425
+ {t("navigation.ask")}
2426
+ </Link>
2427
+ {location.pathname === "/ask" && <AnalyticsChatsSection />}
2428
+ </div>
2129
2429
 
2130
2430
  {/* Sessions link */}
2131
2431
  <Link
@@ -150,6 +150,17 @@ const messages = {
150
150
  },
151
151
  chat: {
152
152
  emptyState: "讓我分析儀表板、比較趨勢或深入檢視資料...",
153
+ recentChats: "最近聊天",
154
+ newChat: "新聊天",
155
+ untitledChat: "未命名聊天",
156
+ renameChat: "重新命名聊天",
157
+ renameThread: "重新命名 {{title}}",
158
+ renameFailed: "無法重新命名聊天",
159
+ optionsFor: "{{title}} 的選項",
160
+ pinChat: "釘選聊天",
161
+ unpinChat: "取消釘選聊天",
162
+ archiveChat: "封存聊天",
163
+ archiveFailed: "無法封存聊天",
153
164
  suggestionArrGrowth: "本季度 ARR 增長的驅動因素是什麼?",
154
165
  suggestionChurn: "顯示過去 6 個月的流失趨勢",
155
166
  suggestionAnomalies: "分析 HubSpot 銷售儀表板的異常",
@@ -187,6 +187,17 @@ const enUS = {
187
187
  chat: {
188
188
  emptyState:
189
189
  "Ask me to analyze a dashboard, compare trends, or dig into data...",
190
+ recentChats: "Recent chats",
191
+ newChat: "New chat",
192
+ untitledChat: "Untitled chat",
193
+ renameChat: "Rename chat",
194
+ renameThread: "Rename {{title}}",
195
+ renameFailed: "Couldn't rename chat",
196
+ optionsFor: "Options for {{title}}",
197
+ pinChat: "Pin chat",
198
+ unpinChat: "Unpin chat",
199
+ archiveChat: "Archive chat",
200
+ archiveFailed: "Couldn't archive chat",
190
201
  suggestionArrGrowth: "What's driving ARR growth this quarter?",
191
202
  suggestionChurn: "Show me churn trends over the last 6 months",
192
203
  suggestionAnomalies: "Analyze the HubSpot Sales dashboard for anomalies",
@@ -3562,6 +3573,17 @@ export const messagesByLocale = {
3562
3573
  },
3563
3574
  chat: {
3564
3575
  emptyState: "让我分析仪表板、比较趋势或深入查看数据...",
3576
+ recentChats: "最近聊天",
3577
+ newChat: "新聊天",
3578
+ untitledChat: "未命名聊天",
3579
+ renameChat: "重命名聊天",
3580
+ renameThread: "重命名 {{title}}",
3581
+ renameFailed: "无法重命名聊天",
3582
+ optionsFor: "{{title}} 的选项",
3583
+ pinChat: "置顶聊天",
3584
+ unpinChat: "取消置顶聊天",
3585
+ archiveChat: "归档聊天",
3586
+ archiveFailed: "无法归档聊天",
3565
3587
  suggestionArrGrowth: "本季度 ARR 增长的驱动因素是什么?",
3566
3588
  suggestionChurn: "显示过去 6 个月的流失趋势",
3567
3589
  suggestionAnomalies: "分析 HubSpot 销售仪表板的异常",
@@ -3746,6 +3768,17 @@ export const messagesByLocale = {
3746
3768
  chat: {
3747
3769
  emptyState:
3748
3770
  "Pídeme que analice un panel, compare tendencias o investigue datos...",
3771
+ recentChats: "Chats recientes",
3772
+ newChat: "Nuevo chat",
3773
+ untitledChat: "Chat sin título",
3774
+ renameChat: "Cambiar nombre del chat",
3775
+ renameThread: "Cambiar nombre de {{title}}",
3776
+ renameFailed: "No se pudo cambiar el nombre del chat",
3777
+ optionsFor: "Opciones para {{title}}",
3778
+ pinChat: "Fijar chat",
3779
+ unpinChat: "Desfijar chat",
3780
+ archiveChat: "Archivar chat",
3781
+ archiveFailed: "No se pudo archivar el chat",
3749
3782
  suggestionArrGrowth: "¿Qué impulsa el crecimiento de ARR este trimestre?",
3750
3783
  suggestionChurn: "Muéstrame la tendencia de bajas de los últimos 6 meses",
3751
3784
  suggestionAnomalies: "Analiza anomalías en el panel de ventas de HubSpot",
@@ -3931,6 +3964,17 @@ export const messagesByLocale = {
3931
3964
  chat: {
3932
3965
  emptyState:
3933
3966
  "Demandez-moi d'analyser un tableau de bord, de comparer des tendances ou d'explorer les données...",
3967
+ recentChats: "Discussions récentes",
3968
+ newChat: "Nouvelle discussion",
3969
+ untitledChat: "Discussion sans titre",
3970
+ renameChat: "Renommer la discussion",
3971
+ renameThread: "Renommer {{title}}",
3972
+ renameFailed: "Impossible de renommer la discussion",
3973
+ optionsFor: "Options pour {{title}}",
3974
+ pinChat: "Épingler la discussion",
3975
+ unpinChat: "Désépingler la discussion",
3976
+ archiveChat: "Archiver la discussion",
3977
+ archiveFailed: "Impossible d'archiver la discussion",
3934
3978
  suggestionArrGrowth:
3935
3979
  "Qu'est-ce qui stimule la croissance de l'ARR ce trimestre ?",
3936
3980
  suggestionChurn: "Montre-moi les tendances de churn des 6 derniers mois",
@@ -4125,6 +4169,17 @@ export const messagesByLocale = {
4125
4169
  chat: {
4126
4170
  emptyState:
4127
4171
  "Bitte mich, ein Dashboard zu analysieren, Trends zu vergleichen oder Daten zu untersuchen...",
4172
+ recentChats: "Neueste Chats",
4173
+ newChat: "Neuer Chat",
4174
+ untitledChat: "Unbenannter Chat",
4175
+ renameChat: "Chat umbenennen",
4176
+ renameThread: "{{title}} umbenennen",
4177
+ renameFailed: "Chat konnte nicht umbenannt werden",
4178
+ optionsFor: "Optionen für {{title}}",
4179
+ pinChat: "Chat anheften",
4180
+ unpinChat: "Chat lösen",
4181
+ archiveChat: "Chat archivieren",
4182
+ archiveFailed: "Chat konnte nicht archiviert werden",
4128
4183
  suggestionArrGrowth: "Was treibt das ARR-Wachstum in diesem Quartal?",
4129
4184
  suggestionChurn: "Zeige mir Churn-Trends der letzten 6 Monate",
4130
4185
  suggestionAnomalies: "Analysiere Anomalien im HubSpot-Sales-Dashboard",
@@ -4311,6 +4366,17 @@ export const messagesByLocale = {
4311
4366
  chat: {
4312
4367
  emptyState:
4313
4368
  "ダッシュボード分析、トレンド比較、データ調査を依頼できます...",
4369
+ recentChats: "最近のチャット",
4370
+ newChat: "新しいチャット",
4371
+ untitledChat: "無題のチャット",
4372
+ renameChat: "チャット名を変更",
4373
+ renameThread: "{{title}} の名前を変更",
4374
+ renameFailed: "チャット名を変更できませんでした",
4375
+ optionsFor: "{{title}} のオプション",
4376
+ pinChat: "チャットをピン留め",
4377
+ unpinChat: "チャットのピン留めを解除",
4378
+ archiveChat: "チャットをアーカイブ",
4379
+ archiveFailed: "チャットをアーカイブできませんでした",
4314
4380
  suggestionArrGrowth: "今四半期の ARR 成長要因は?",
4315
4381
  suggestionChurn: "過去 6 か月の解約傾向を表示",
4316
4382
  suggestionAnomalies: "HubSpot Sales ダッシュボードの異常を分析",
@@ -4497,6 +4563,17 @@ export const messagesByLocale = {
4497
4563
  chat: {
4498
4564
  emptyState:
4499
4565
  "대시보드를 분석하거나 추세를 비교하거나 데이터를 파고들어 달라고 요청하세요...",
4566
+ recentChats: "최근 채팅",
4567
+ newChat: "새 채팅",
4568
+ untitledChat: "제목 없는 채팅",
4569
+ renameChat: "채팅 이름 바꾸기",
4570
+ renameThread: "{{title}} 이름 바꾸기",
4571
+ renameFailed: "채팅 이름을 바꾸지 못했습니다",
4572
+ optionsFor: "{{title}} 옵션",
4573
+ pinChat: "채팅 고정",
4574
+ unpinChat: "채팅 고정 해제",
4575
+ archiveChat: "채팅 보관",
4576
+ archiveFailed: "채팅을 보관하지 못했습니다",
4500
4577
  suggestionArrGrowth: "이번 분기 ARR 성장을 이끄는 요인은?",
4501
4578
  suggestionChurn: "지난 6개월의 이탈 추세를 보여줘",
4502
4579
  suggestionAnomalies: "HubSpot Sales 대시보드의 이상 징후를 분석해줘",
@@ -4684,6 +4761,17 @@ export const messagesByLocale = {
4684
4761
  chat: {
4685
4762
  emptyState:
4686
4763
  "Peça para eu analisar um dashboard, comparar tendências ou investigar dados...",
4764
+ recentChats: "Chats recentes",
4765
+ newChat: "Novo chat",
4766
+ untitledChat: "Chat sem título",
4767
+ renameChat: "Renomear chat",
4768
+ renameThread: "Renomear {{title}}",
4769
+ renameFailed: "Não foi possível renomear o chat",
4770
+ optionsFor: "Opções para {{title}}",
4771
+ pinChat: "Fixar chat",
4772
+ unpinChat: "Desafixar chat",
4773
+ archiveChat: "Arquivar chat",
4774
+ archiveFailed: "Não foi possível arquivar o chat",
4687
4775
  suggestionArrGrowth:
4688
4776
  "O que impulsiona o crescimento de ARR neste trimestre?",
4689
4777
  suggestionChurn: "Mostre tendências de churn dos últimos 6 meses",
@@ -4869,6 +4957,17 @@ export const messagesByLocale = {
4869
4957
  chat: {
4870
4958
  emptyState:
4871
4959
  "मुझसे डैशबोर्ड का विश्लेषण, ट्रेंड की तुलना या डेटा में गहराई से देखने को कहें...",
4960
+ recentChats: "हाल की चैट",
4961
+ newChat: "नई चैट",
4962
+ untitledChat: "बिना शीर्षक वाली चैट",
4963
+ renameChat: "चैट का नाम बदलें",
4964
+ renameThread: "{{title}} का नाम बदलें",
4965
+ renameFailed: "चैट का नाम नहीं बदला जा सका",
4966
+ optionsFor: "{{title}} के विकल्प",
4967
+ pinChat: "चैट पिन करें",
4968
+ unpinChat: "चैट अनपिन करें",
4969
+ archiveChat: "चैट आर्काइव करें",
4970
+ archiveFailed: "चैट आर्काइव नहीं की जा सकी",
4872
4971
  suggestionArrGrowth: "इस तिमाही ARR वृद्धि को क्या चला रहा है?",
4873
4972
  suggestionChurn: "पिछले 6 महीनों के churn ट्रेंड दिखाएं",
4874
4973
  suggestionAnomalies: "HubSpot Sales डैशबोर्ड में anomalies का विश्लेषण करें",
@@ -5052,6 +5151,17 @@ export const messagesByLocale = {
5052
5151
  chat: {
5053
5152
  emptyState:
5054
5153
  "اطلب مني تحليل لوحة معلومات أو مقارنة الاتجاهات أو التعمق في البيانات...",
5154
+ recentChats: "الدردشات الأخيرة",
5155
+ newChat: "دردشة جديدة",
5156
+ untitledChat: "دردشة بدون عنوان",
5157
+ renameChat: "إعادة تسمية الدردشة",
5158
+ renameThread: "إعادة تسمية {{title}}",
5159
+ renameFailed: "تعذرت إعادة تسمية الدردشة",
5160
+ optionsFor: "خيارات {{title}}",
5161
+ pinChat: "تثبيت الدردشة",
5162
+ unpinChat: "إلغاء تثبيت الدردشة",
5163
+ archiveChat: "أرشفة الدردشة",
5164
+ archiveFailed: "تعذرت أرشفة الدردشة",
5055
5165
  suggestionArrGrowth: "ما الذي يدفع نمو ARR هذا الربع؟",
5056
5166
  suggestionChurn: "اعرض اتجاهات فقدان العملاء خلال آخر 6 أشهر",
5057
5167
  suggestionAnomalies: "حلل الشذوذ في لوحة مبيعات HubSpot",
@@ -67,7 +67,10 @@ import {
67
67
  type TweakSelections,
68
68
  } from "@shared/resolve-tweaks";
69
69
  import { utilityStem, widthToPrefix } from "@shared/responsive-classes";
70
- import { normalizeDesignSourceType } from "@shared/source-mode";
70
+ import {
71
+ normalizeDesignSourceType,
72
+ type DesignSourceType,
73
+ } from "@shared/source-mode";
71
74
  import {
72
75
  IconArrowLeft,
73
76
  IconArrowUpRight,
@@ -347,6 +350,28 @@ function getContentSignature(content: string): string {
347
350
  return `${content.length}:${hash.toString(36)}`;
348
351
  }
349
352
 
353
+ export function getOverviewScreenRuntimeReplacementKey({
354
+ screenId,
355
+ updatedAt,
356
+ content,
357
+ }: {
358
+ screenId: string;
359
+ updatedAt?: string | null;
360
+ content: string;
361
+ }) {
362
+ return [screenId, updatedAt ?? "", getContentSignature(content)].join(":");
363
+ }
364
+
365
+ export function shouldUseOverviewRuntimeReplacement({
366
+ sourceType,
367
+ externalSnapshotHtml,
368
+ }: {
369
+ sourceType?: DesignSourceType | null;
370
+ externalSnapshotHtml?: string | null;
371
+ }) {
372
+ return sourceType === "inline" && !externalSnapshotHtml;
373
+ }
374
+
350
375
  function dedupeStringIds(ids: string[]): string[] {
351
376
  return Array.from(new Set(ids.filter(Boolean)));
352
377
  }
@@ -17258,12 +17283,26 @@ ${serializedHtml}
17258
17283
  const screenBridgeUrl = screen.bridgeUrl;
17259
17284
  const screenSnapshot =
17260
17285
  liveScreenSnapshotsById[screen.id]?.html;
17286
+ const screenContentSignature =
17287
+ getContentSignature(screenContent);
17288
+ const useRuntimeReplacement =
17289
+ shouldUseOverviewRuntimeReplacement({
17290
+ sourceType: screenSourceType,
17291
+ externalSnapshotHtml: screenSnapshot,
17292
+ });
17293
+ const runtimeReplacementKey = useRuntimeReplacement
17294
+ ? getOverviewScreenRuntimeReplacementKey({
17295
+ screenId: screen.id,
17296
+ updatedAt: screen.updatedAt,
17297
+ content: screenContent,
17298
+ })
17299
+ : undefined;
17261
17300
  const screenContentKey = screenIsActive
17262
17301
  ? [screen.id, contentRenderRevision].join(":")
17263
17302
  : [
17264
17303
  screen.id,
17265
17304
  screen.updatedAt ?? "",
17266
- getContentSignature(screenContent),
17305
+ screenContentSignature,
17267
17306
  0,
17268
17307
  ].join(":");
17269
17308
 
@@ -17271,6 +17310,10 @@ ${serializedHtml}
17271
17310
  <DesignCanvas
17272
17311
  content={screenContent}
17273
17312
  contentKey={screenContentKey}
17313
+ runtimeReplacementContent={
17314
+ useRuntimeReplacement ? screenContent : undefined
17315
+ }
17316
+ runtimeReplacementKey={runtimeReplacementKey}
17274
17317
  screenId={screen.id}
17275
17318
  zoom={100}
17276
17319
  deviceFrame="none"
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-01
4
+ ---
5
+
6
+ Design previews refresh immediately after agent screen edits.