@iloveagents/foundry-web-ui 0.2.0 → 0.2.2
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/CHANGELOG.md +100 -0
- package/package.json +3 -3
- package/src/components/sidebar.tsx +101 -12
- package/src/index.ts +1 -0
- package/src/lib/chat-lifecycle-store.ts +37 -0
- package/src/lib/use-new-conversation.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
1
|
# @iloveagents/foundry-web-ui
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 395f0cd: web-ui: popover New Thread didn't actually start a new thread
|
|
8
|
+
|
|
9
|
+
When the user clicks the compose / SquarePen "New Thread" button in
|
|
10
|
+
the floating chat popover (`ChatBubble` on workspace/entity pages with
|
|
11
|
+
`navigateToChat: false`), the runtime was supposed to discard the
|
|
12
|
+
current thread and start fresh. In practice the popover header
|
|
13
|
+
subtitle flipped to "New conversation" but the message list stayed
|
|
14
|
+
populated with the prior chat's messages — the button looked like a
|
|
15
|
+
no-op.
|
|
16
|
+
|
|
17
|
+
Root cause: `useNewConversation` bumped the AGUIRuntime inner key to
|
|
18
|
+
remount, but the parent `RuntimeBody`'s `effectiveThreadId`
|
|
19
|
+
(`urlMatch ?? sticky ?? freshIdRef.current`) stayed pinned to the
|
|
20
|
+
sticky active-chat id. The history adapter on the fresh inner mount
|
|
21
|
+
immediately loaded the same chat's messages from the server →
|
|
22
|
+
nothing visibly changed.
|
|
23
|
+
|
|
24
|
+
The sidebar "New Thread" button avoided this only by side-effect —
|
|
25
|
+
it navigates to `/`, which triggers `useTrackActiveChatFromUrl` to
|
|
26
|
+
clear sticky. The popover preserves the user's workspace context and
|
|
27
|
+
doesn't navigate, so sticky never cleared.
|
|
28
|
+
|
|
29
|
+
Fix: add a tiny DI store `useChatLifecycleStore` exposing an
|
|
30
|
+
`onNewConversation` callback. `useNewConversation` invokes it BEFORE
|
|
31
|
+
`resetThread` so the host (spaces-web-ui in lastspace) can clear its
|
|
32
|
+
sticky id; the next render's `effectiveThreadId` has already advanced
|
|
33
|
+
to a freshly-minted UUID, and the history adapter binds to that
|
|
34
|
+
instead of re-hydrating the prior chat.
|
|
35
|
+
|
|
36
|
+
Customers that don't track sticky ids leave `onNewConversation`
|
|
37
|
+
unset; the call site no-ops gracefully.
|
|
38
|
+
|
|
39
|
+
- 1ba01ef: web-ui(sidebar): align group-header labels (CHATS, WORKSPACES, ADMIN, …)
|
|
40
|
+
with the leftmost edge of their item labels
|
|
41
|
+
|
|
42
|
+
Previously the collapsible group header used `px-3` on the outer wrapper
|
|
43
|
+
and a `size-6` chevron, while item rows used `pl-5` (depth-0 indent) +
|
|
44
|
+
`size-3.5` icon + `gap-2`. Result: group labels landed at ~36px from the
|
|
45
|
+
sidebar edge while item labels landed at 42px — group labels drifted
|
|
46
|
+
right of the children they introduced and the visual rhythm broke.
|
|
47
|
+
|
|
48
|
+
Make the header's leading column a shared `SIDEBAR_ITEM_ICON_CLASS` +
|
|
49
|
+
`SIDEBAR_ITEM_GAP_CLASS` cell — the same constants item icons use — so
|
|
50
|
+
every group label (collapsible, link-style, or plain) lands at the same
|
|
51
|
+
x-coordinate as item labels. The chevron now sits in the icon column;
|
|
52
|
+
its hit-area is preserved at 28px via `-m-2 p-2` (negative-margin
|
|
53
|
+
absorbs the extra padding so the visual cell stays 14px and label
|
|
54
|
+
geometry doesn't shift).
|
|
55
|
+
|
|
56
|
+
Non-collapsible header branches get an explicit invisible spacer of the
|
|
57
|
+
same width so their labels align too.
|
|
58
|
+
|
|
59
|
+
- 8184a8c: shell: URL is authoritative for the runtime threadId — fixes
|
|
60
|
+
resume-creates-new-conversation race
|
|
61
|
+
|
|
62
|
+
`ChatConversationAwareRuntime` previously computed
|
|
63
|
+
`effectiveThreadId = sticky ?? urlMatch ?? freshIdRef.current`.
|
|
64
|
+
When the user navigated from one chat to another via a Recents
|
|
65
|
+
click, the synchronous render that followed the URL change had:
|
|
66
|
+
- `urlMatch = B` (read from the now-updated `useLocation`)
|
|
67
|
+
- `sticky = A` (the active-chat-store hadn't been updated by
|
|
68
|
+
`useTrackActiveChatFromUrl`'s `useEffect` yet — effects run
|
|
69
|
+
AFTER the commit phase)
|
|
70
|
+
|
|
71
|
+
`sticky ?? urlMatch` picked `A` (the previous chat). The AG-UI
|
|
72
|
+
adapter was constructed with `threadId = A`. The first message the
|
|
73
|
+
user sent went to `/api/agent` with `thread_id = A`, the middleware
|
|
74
|
+
created a new row keyed by `A`... wait actually no, here's what
|
|
75
|
+
happens — the runtime mints a fresh runner UUID when given a
|
|
76
|
+
mismatched threadId; that fresh UUID became a new conversation row,
|
|
77
|
+
appearing as "Untitled chat" at the top of the sidebar while the
|
|
78
|
+
URL still said `/chat/B`. Two active-looking dots: one for `B`
|
|
79
|
+
(NavLink URL match), one for the new row (statusDot from the
|
|
80
|
+
updated active-chat-store after the lazy ensure ran).
|
|
81
|
+
|
|
82
|
+
Swap the priority: `urlMatch ?? sticky ?? freshIdRef.current`. The
|
|
83
|
+
URL is authoritative whenever it's set (i.e. on `/chat/<id>`
|
|
84
|
+
routes), eliminating the stale-sticky race entirely. `sticky` is
|
|
85
|
+
still consulted as the second-priority fallback for non-chat
|
|
86
|
+
routes (`/spaces`, `/tasks`) so the popout chat stays "live" while
|
|
87
|
+
the user browses workspaces — that's the original purpose of
|
|
88
|
+
`useStickyConversationId` and it's preserved.
|
|
89
|
+
|
|
90
|
+
- Updated dependencies [8184a8c]
|
|
91
|
+
- @iloveagents/foundry-agent@0.2.2
|
|
92
|
+
- @iloveagents/foundry-web-primitives@0.2.2
|
|
93
|
+
|
|
94
|
+
## 0.2.1
|
|
95
|
+
|
|
96
|
+
### Patch Changes
|
|
97
|
+
|
|
98
|
+
- No source changes. Bumped in lock-step with the fixed group so the
|
|
99
|
+
whole group can publish at a version number that's free across all
|
|
100
|
+
four packages. (`ui@0.2.0` already published cleanly; this is a
|
|
101
|
+
follow-the-group patch bump.)
|
|
102
|
+
|
|
3
103
|
## 0.2.0
|
|
4
104
|
|
|
5
105
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iloveagents/foundry-web-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"react-markdown": "^10.0.0",
|
|
47
47
|
"remark-gfm": "^4.0.0",
|
|
48
48
|
"shiki": "^4.0.0",
|
|
49
|
-
"@iloveagents/foundry-agent": "0.2.
|
|
50
|
-
"@iloveagents/foundry-web-primitives": "0.2.
|
|
49
|
+
"@iloveagents/foundry-agent": "0.2.2",
|
|
50
|
+
"@iloveagents/foundry-web-primitives": "0.2.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"typescript": "~5.9.3",
|
|
@@ -27,6 +27,39 @@ const ACTIVE_NAV_BADGE_CLASS =
|
|
|
27
27
|
"border border-sidebar-border bg-background/60 text-sidebar-foreground";
|
|
28
28
|
const NESTED_NAV_INDENTS = ["pl-5", "pl-7", "pl-9", "pl-11", "pl-13"] as const;
|
|
29
29
|
|
|
30
|
+
// Shared icon-column geometry. Top-level item rows render their
|
|
31
|
+
// leading icon at ``size-4`` (16px) followed by ``gap-2`` (8px)
|
|
32
|
+
// before the label. The group-header chevron uses the SAME column
|
|
33
|
+
// width + gap so the group label and item labels share an exact
|
|
34
|
+
// x-coordinate — without this invariant headers visually drift right
|
|
35
|
+
// (previously the chevron defaulted to ``size-6`` = 24px) and the
|
|
36
|
+
// sidebar looks ragged. Verified empirically against the live DOM:
|
|
37
|
+
// item icon x-min ≈ header chevron x-min, item label x ≈ header
|
|
38
|
+
// label x (within 1px allowing for the item's ``border-transparent``
|
|
39
|
+
// inset).
|
|
40
|
+
const SIDEBAR_ITEM_ICON_CLASS = "size-4 shrink-0";
|
|
41
|
+
const SIDEBAR_ITEM_GAP_CLASS = "gap-2";
|
|
42
|
+
// Outer padding for the group-header wrapper. Horizontal matches
|
|
43
|
+
// ``px-3`` on item buttons so the chevron column lines up with the
|
|
44
|
+
// item icon column. Vertical is asymmetric: ``pt-3`` (12px) above
|
|
45
|
+
// gives breathing room between sections, ``pb-1`` (4px) below bonds
|
|
46
|
+
// the header to its first child — the ChatGPT / Linear / Notion
|
|
47
|
+
// "header sits on its children" pattern. Symmetric ``py-2`` made
|
|
48
|
+
// the header float midway between sections instead of belonging
|
|
49
|
+
// to the group below it.
|
|
50
|
+
const SIDEBAR_GROUP_HEADER_PADDING = "px-3 pt-4 pb-0";
|
|
51
|
+
// Typography for the group-header LABEL text (CHATS / WORKSPACES /
|
|
52
|
+
// ADMIN / …). Stays at ``text-xs`` (12px) — same size as before —
|
|
53
|
+
// but bumps weight from ``font-medium`` to ``font-semibold``. The
|
|
54
|
+
// extra weight gives section headers the structural presence they
|
|
55
|
+
// need to feel like section dividers without spending vertical
|
|
56
|
+
// space; combined with ``uppercase tracking-wider`` it's the
|
|
57
|
+
// ChatGPT / Linear / Notion small-caps section-label rhythm.
|
|
58
|
+
// Bumping size AND weight together stacked too much visual mass
|
|
59
|
+
// against the already-wide uppercase tracking — felt shouty.
|
|
60
|
+
const SIDEBAR_GROUP_HEADER_LABEL_CLASS =
|
|
61
|
+
"text-xs font-semibold uppercase tracking-wider";
|
|
62
|
+
|
|
30
63
|
type NavStatusDot = NonNullable<NavItem["statusDot"]>;
|
|
31
64
|
type NavStatusIcon = NonNullable<NavItem["statusIcon"]>;
|
|
32
65
|
|
|
@@ -1184,7 +1217,12 @@ function NavGroupSection({ group }: { group: NavGroup }) {
|
|
|
1184
1217
|
return (
|
|
1185
1218
|
<div data-nav-group-label={group.label}>
|
|
1186
1219
|
{!group.hideLabel && (
|
|
1187
|
-
|
|
1220
|
+
// Outer padding matches the depth-0 item button's ``px-3`` so
|
|
1221
|
+
// the chevron / leading spacer sits in the SAME icon column as
|
|
1222
|
+
// item icons, and the group label ends up at the exact x-coordinate
|
|
1223
|
+
// as item labels. See ``SIDEBAR_GROUP_HEADER_PADDING`` definition
|
|
1224
|
+
// for the alignment invariant.
|
|
1225
|
+
<div className={SIDEBAR_GROUP_HEADER_PADDING}>
|
|
1188
1226
|
{isCollapsible ? (
|
|
1189
1227
|
// Collapsible header is a flex of three siblings: chevron
|
|
1190
1228
|
// toggle (own button), label (NavLink when ``group.to``
|
|
@@ -1192,19 +1230,45 @@ function NavGroupSection({ group }: { group: NavGroup }) {
|
|
|
1192
1230
|
// landing page — Workspaces does this), createActions
|
|
1193
1231
|
// menu. Three SIBLINGS, not nested, so HTML stays valid
|
|
1194
1232
|
// (no <button> inside <button> / inside Radix's button).
|
|
1195
|
-
|
|
1233
|
+
//
|
|
1234
|
+
// Geometry: the chevron occupies the SAME width as item
|
|
1235
|
+
// icons (``SIDEBAR_ITEM_ICON_CLASS``) followed by
|
|
1236
|
+
// ``SIDEBAR_ITEM_GAP_CLASS``, so the group label and the
|
|
1237
|
+
// item labels share an exact x-coordinate. The chevron's
|
|
1238
|
+
// click target is larger than its visual size — extended
|
|
1239
|
+
// via padding on the wrapper button — so users don't have
|
|
1240
|
+
// to hit a tiny target. The clickable invisible padding
|
|
1241
|
+
// OVERLAPS the gap rather than pushing the label right
|
|
1242
|
+
// (negative-margin + extra padding cancel out).
|
|
1243
|
+
<div
|
|
1244
|
+
className={cn(
|
|
1245
|
+
"group/header flex items-center justify-between rounded-lg transition-colors hover:bg-sidebar-accent/70",
|
|
1246
|
+
SIDEBAR_ITEM_GAP_CLASS,
|
|
1247
|
+
)}
|
|
1248
|
+
>
|
|
1196
1249
|
<button
|
|
1197
1250
|
type="button"
|
|
1198
1251
|
data-nav-group-toggle
|
|
1199
1252
|
onClick={() => toggleGroupCollapsed(group.label, persistedCollapsed)}
|
|
1200
1253
|
aria-expanded={!collapsed}
|
|
1201
1254
|
aria-label={collapsed ? `Expand ${group.label}` : `Collapse ${group.label}`}
|
|
1202
|
-
|
|
1255
|
+
// Visual cell width = ``SIDEBAR_ITEM_ICON_CLASS`` (14px)
|
|
1256
|
+
// so the chevron sits in the same column as item icons
|
|
1257
|
+
// and the group label aligns with item labels. The
|
|
1258
|
+
// chevron SVG (``size-3`` = 12px) is smaller than the
|
|
1259
|
+
// cell so it reads as a hint, not a primary control.
|
|
1260
|
+
// Hit area is the cell width; the row's ``group/header``
|
|
1261
|
+
// hover state covers the rest of the strip so users get
|
|
1262
|
+
// visual feedback even on near-misses.
|
|
1263
|
+
className={cn(
|
|
1264
|
+
"inline-flex items-center justify-center rounded text-sidebar-foreground/50 hover:text-sidebar-foreground",
|
|
1265
|
+
SIDEBAR_ITEM_ICON_CLASS,
|
|
1266
|
+
)}
|
|
1203
1267
|
>
|
|
1204
1268
|
{collapsed ? (
|
|
1205
|
-
<ChevronRight className="size-3 transition-transform" />
|
|
1269
|
+
<ChevronRight className="size-3.5 transition-transform" />
|
|
1206
1270
|
) : (
|
|
1207
|
-
<ChevronDown className="size-3 transition-transform" />
|
|
1271
|
+
<ChevronDown className="size-3.5 transition-transform" />
|
|
1208
1272
|
)}
|
|
1209
1273
|
</button>
|
|
1210
1274
|
{group.to ? (
|
|
@@ -1213,7 +1277,8 @@ function NavGroupSection({ group }: { group: NavGroup }) {
|
|
|
1213
1277
|
<>
|
|
1214
1278
|
<span
|
|
1215
1279
|
className={cn(
|
|
1216
|
-
"truncate
|
|
1280
|
+
"truncate",
|
|
1281
|
+
SIDEBAR_GROUP_HEADER_LABEL_CLASS,
|
|
1217
1282
|
isActive
|
|
1218
1283
|
? "text-sidebar-selected-foreground"
|
|
1219
1284
|
: "text-sidebar-foreground/65 group-hover/header:text-sidebar-foreground",
|
|
@@ -1231,7 +1296,12 @@ function NavGroupSection({ group }: { group: NavGroup }) {
|
|
|
1231
1296
|
onClick={() => toggleGroupCollapsed(group.label, persistedCollapsed)}
|
|
1232
1297
|
className="flex min-w-0 flex-1 items-center gap-1.5 py-1.5 text-left"
|
|
1233
1298
|
>
|
|
1234
|
-
<span
|
|
1299
|
+
<span
|
|
1300
|
+
className={cn(
|
|
1301
|
+
"truncate text-sidebar-foreground/65 group-hover/header:text-sidebar-foreground",
|
|
1302
|
+
SIDEBAR_GROUP_HEADER_LABEL_CLASS,
|
|
1303
|
+
)}
|
|
1304
|
+
>
|
|
1235
1305
|
{group.label}
|
|
1236
1306
|
</span>
|
|
1237
1307
|
<NavStatusDot statusDot={bubbledStatusDot} />
|
|
@@ -1242,12 +1312,23 @@ function NavGroupSection({ group }: { group: NavGroup }) {
|
|
|
1242
1312
|
)}
|
|
1243
1313
|
</div>
|
|
1244
1314
|
) : group.to ? (
|
|
1245
|
-
|
|
1315
|
+
// Non-collapsible header: no chevron, but we still emit an
|
|
1316
|
+
// invisible spacer of ``SIDEBAR_ITEM_ICON_CLASS`` width + the
|
|
1317
|
+
// shared gap so the group label lands in the same column as
|
|
1318
|
+
// collapsible headers and item labels.
|
|
1319
|
+
<div
|
|
1320
|
+
className={cn(
|
|
1321
|
+
"flex items-center justify-between rounded-lg py-1.5 transition-colors hover:bg-sidebar-accent/70",
|
|
1322
|
+
SIDEBAR_ITEM_GAP_CLASS,
|
|
1323
|
+
)}
|
|
1324
|
+
>
|
|
1325
|
+
<span aria-hidden="true" className={SIDEBAR_ITEM_ICON_CLASS} />
|
|
1246
1326
|
<NavLink to={group.to} end className="min-w-0 flex-1">
|
|
1247
1327
|
{({ isActive }) => (
|
|
1248
1328
|
<span
|
|
1249
1329
|
className={cn(
|
|
1250
|
-
"block
|
|
1330
|
+
"block",
|
|
1331
|
+
SIDEBAR_GROUP_HEADER_LABEL_CLASS,
|
|
1251
1332
|
isActive
|
|
1252
1333
|
? "text-sidebar-selected-foreground"
|
|
1253
1334
|
: "text-sidebar-foreground/65 hover:text-sidebar-foreground",
|
|
@@ -1262,9 +1343,17 @@ function NavGroupSection({ group }: { group: NavGroup }) {
|
|
|
1262
1343
|
)}
|
|
1263
1344
|
</div>
|
|
1264
1345
|
) : (
|
|
1265
|
-
<
|
|
1266
|
-
{
|
|
1267
|
-
|
|
1346
|
+
<div className={cn("flex items-center", SIDEBAR_ITEM_GAP_CLASS)}>
|
|
1347
|
+
<span aria-hidden="true" className={SIDEBAR_ITEM_ICON_CLASS} />
|
|
1348
|
+
<span
|
|
1349
|
+
className={cn(
|
|
1350
|
+
"text-sidebar-foreground/65",
|
|
1351
|
+
SIDEBAR_GROUP_HEADER_LABEL_CLASS,
|
|
1352
|
+
)}
|
|
1353
|
+
>
|
|
1354
|
+
{group.label}
|
|
1355
|
+
</span>
|
|
1356
|
+
</div>
|
|
1268
1357
|
)}
|
|
1269
1358
|
</div>
|
|
1270
1359
|
)}
|
package/src/index.ts
CHANGED
|
@@ -116,6 +116,7 @@ export type {
|
|
|
116
116
|
} from "./lib/tool-panel-store.ts";
|
|
117
117
|
export { useSidebarStore, SIDEBAR_WIDTH, RAIL_WIDTH } from "./lib/sidebar-store.ts";
|
|
118
118
|
export { useChatBubbleStore } from "./lib/chat-bubble-store.ts";
|
|
119
|
+
export { useChatLifecycleStore } from "./lib/chat-lifecycle-store.ts";
|
|
119
120
|
export { submitComposerText } from "./lib/composer-submit-store.ts";
|
|
120
121
|
export {
|
|
121
122
|
registerSelectionContextResolver,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle DI store for chat-related cross-cutting concerns that
|
|
3
|
+
* foundry-ui needs to invoke but spaces-web-ui owns the implementation
|
|
4
|
+
* for. Avoids a circular dependency between the two packages.
|
|
5
|
+
*
|
|
6
|
+
* Today's only hook: ``onNewConversation`` — called when the user
|
|
7
|
+
* starts a fresh chat (sidebar New Thread, popover compose button,
|
|
8
|
+
* etc.). spaces-web-ui registers a callback at app init that clears
|
|
9
|
+
* the sticky active-chat id so the next render mints a fresh runtime
|
|
10
|
+
* thread id (otherwise the history adapter re-hydrates the previous
|
|
11
|
+
* chat's messages immediately, defeating the "new thread" intent).
|
|
12
|
+
*
|
|
13
|
+
* Registration: ``useChatLifecycleStore.setState({ onNewConversation })``
|
|
14
|
+
* once at app init. Foundry-ui's :func:`useNewConversation` invokes
|
|
15
|
+
* ``onNewConversation`` synchronously inside its returned callback,
|
|
16
|
+
* BEFORE :func:`resetThread` runs — so by the time the AGUIRuntimeProvider
|
|
17
|
+
* re-renders, the sticky id is already null and the effective thread id
|
|
18
|
+
* has already advanced to a freshly-minted UUID.
|
|
19
|
+
*
|
|
20
|
+
* Customers that don't use sticky id tracking can leave
|
|
21
|
+
* ``onNewConversation`` null — the call site no-ops gracefully.
|
|
22
|
+
*/
|
|
23
|
+
import { create } from "zustand";
|
|
24
|
+
|
|
25
|
+
interface ChatLifecycleStore {
|
|
26
|
+
/**
|
|
27
|
+
* Called from :func:`useNewConversation` when the user starts a
|
|
28
|
+
* new thread. Spaces wires this to clear the active-chat sticky id.
|
|
29
|
+
*/
|
|
30
|
+
onNewConversation: (() => void) | null;
|
|
31
|
+
setOnNewConversation: (fn: (() => void) | null) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const useChatLifecycleStore = create<ChatLifecycleStore>((set) => ({
|
|
35
|
+
onNewConversation: null,
|
|
36
|
+
setOnNewConversation: (fn) => set({ onNewConversation: fn }),
|
|
37
|
+
}));
|
|
@@ -7,6 +7,7 @@ import { useAGUIAdapter } from "../components/ag-ui-runtime-provider.tsx";
|
|
|
7
7
|
import { useToolPanelStore } from "./tool-panel-store.ts";
|
|
8
8
|
import { useAppStore } from "./app-store.ts";
|
|
9
9
|
import { useSidebarStore } from "./sidebar-store.ts";
|
|
10
|
+
import { useChatLifecycleStore } from "./chat-lifecycle-store.ts";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Shared hook for starting a new conversation.
|
|
@@ -35,6 +36,15 @@ export function useNewConversation(options?: { navigateToChat?: boolean; preserv
|
|
|
35
36
|
}
|
|
36
37
|
closeMobile();
|
|
37
38
|
clearCitations();
|
|
39
|
+
// Notify the host app that a new thread is starting. Spaces wires
|
|
40
|
+
// this to clear its active-chat sticky id so the AGUIRuntimeProvider's
|
|
41
|
+
// ``effectiveThreadId`` advances to a freshly-minted UUID instead of
|
|
42
|
+
// staying pinned to the previous chat. Must fire BEFORE ``resetThread``
|
|
43
|
+
// so the next render's history adapter binds to the new id and
|
|
44
|
+
// doesn't re-hydrate the prior chat's messages — the exact bug this
|
|
45
|
+
// wiring fixes for the popover (``navigateToChat: false``) path,
|
|
46
|
+
// where there is no ``/`` navigation to clear sticky for us.
|
|
47
|
+
useChatLifecycleStore.getState().onNewConversation?.();
|
|
38
48
|
resetThread();
|
|
39
49
|
aui.threads().switchToNewThread();
|
|
40
50
|
if (options?.navigateToChat ?? true) {
|