@consciousclouds/operator-shell 0.1.0 → 0.2.0
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 +66 -0
- package/README.md +80 -31
- package/dist/chief/ChiefDrawer.d.ts +34 -2
- package/dist/chief/ChiefDrawer.d.ts.map +1 -1
- package/dist/chief/ChiefDrawer.js +43 -30
- package/dist/chief/ChiefDrawer.js.map +1 -1
- package/dist/chief/ChiefHeaderBar.d.ts +19 -0
- package/dist/chief/ChiefHeaderBar.d.ts.map +1 -0
- package/dist/chief/ChiefHeaderBar.js +7 -0
- package/dist/chief/ChiefHeaderBar.js.map +1 -0
- package/dist/chief/ChiefMarkdown.d.ts +12 -0
- package/dist/chief/ChiefMarkdown.d.ts.map +1 -0
- package/dist/chief/ChiefMarkdown.js +95 -0
- package/dist/chief/ChiefMarkdown.js.map +1 -0
- package/dist/chief/ChiefPanel.d.ts +44 -1
- package/dist/chief/ChiefPanel.d.ts.map +1 -1
- package/dist/chief/ChiefPanel.js +79 -29
- package/dist/chief/ChiefPanel.js.map +1 -1
- package/dist/chief/ChiefRegion.d.ts +44 -1
- package/dist/chief/ChiefRegion.d.ts.map +1 -1
- package/dist/chief/ChiefRegion.js +63 -27
- package/dist/chief/ChiefRegion.js.map +1 -1
- package/dist/chief/ChiefSession.d.ts +46 -10
- package/dist/chief/ChiefSession.d.ts.map +1 -1
- package/dist/chief/ChiefSession.js +252 -6
- package/dist/chief/ChiefSession.js.map +1 -1
- package/dist/chief/ChiefTrigger.d.ts +26 -0
- package/dist/chief/ChiefTrigger.d.ts.map +1 -0
- package/dist/chief/ChiefTrigger.js +21 -0
- package/dist/chief/ChiefTrigger.js.map +1 -0
- package/dist/chief/conversation-adapter.d.ts +70 -0
- package/dist/chief/conversation-adapter.d.ts.map +1 -0
- package/dist/chief/conversation-adapter.js +26 -0
- package/dist/chief/conversation-adapter.js.map +1 -0
- package/dist/chief/conversation-identity.d.ts +48 -0
- package/dist/chief/conversation-identity.d.ts.map +1 -0
- package/dist/chief/conversation-identity.js +100 -0
- package/dist/chief/conversation-identity.js.map +1 -0
- package/dist/chief/personality-display.d.ts +56 -0
- package/dist/chief/personality-display.d.ts.map +1 -0
- package/dist/chief/personality-display.js +52 -0
- package/dist/chief/personality-display.js.map +1 -0
- package/dist/chief/turn-adapter.d.ts +45 -0
- package/dist/chief/turn-adapter.d.ts.map +1 -1
- package/dist/chief/use-chief-turn.d.ts +127 -14
- package/dist/chief/use-chief-turn.d.ts.map +1 -1
- package/dist/chief/use-chief-turn.js +284 -28
- package/dist/chief/use-chief-turn.js.map +1 -1
- package/dist/chief/use-controlled-open.d.ts +2 -0
- package/dist/chief/use-controlled-open.d.ts.map +1 -0
- package/dist/chief/use-controlled-open.js +27 -0
- package/dist/chief/use-controlled-open.js.map +1 -0
- package/dist/chief/use-smooth-stream.d.ts +8 -0
- package/dist/chief/use-smooth-stream.d.ts.map +1 -0
- package/dist/chief/use-smooth-stream.js +128 -0
- package/dist/chief/use-smooth-stream.js.map +1 -0
- package/dist/flavor/types.d.ts +36 -21
- package/dist/flavor/types.d.ts.map +1 -1
- package/dist/frame/ComposedWorkspace.d.ts +39 -0
- package/dist/frame/ComposedWorkspace.d.ts.map +1 -0
- package/dist/frame/ComposedWorkspace.js +94 -0
- package/dist/frame/ComposedWorkspace.js.map +1 -0
- package/dist/frame/OperatorShell.d.ts +115 -13
- package/dist/frame/OperatorShell.d.ts.map +1 -1
- package/dist/frame/OperatorShell.js +200 -12
- package/dist/frame/OperatorShell.js.map +1 -1
- package/dist/guard/permissions.d.ts +2 -2
- package/dist/guard/permissions.js +2 -2
- package/dist/header/OperatorHeader.d.ts +15 -2
- package/dist/header/OperatorHeader.d.ts.map +1 -1
- package/dist/header/OperatorHeader.js +3 -3
- package/dist/header/OperatorHeader.js.map +1 -1
- package/dist/icons/registry.d.ts +7 -1
- package/dist/icons/registry.d.ts.map +1 -1
- package/dist/icons/registry.js +31 -2
- package/dist/icons/registry.js.map +1 -1
- package/dist/index.d.ts +24 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -17
- package/dist/index.js.map +1 -1
- package/dist/sidebar/OperatorSidebar.d.ts +16 -1
- package/dist/sidebar/OperatorSidebar.d.ts.map +1 -1
- package/dist/sidebar/OperatorSidebar.js +13 -10
- package/dist/sidebar/OperatorSidebar.js.map +1 -1
- package/dist/workspace/FormWorkspace.d.ts +2 -3
- package/dist/workspace/FormWorkspace.d.ts.map +1 -1
- package/dist/workspace/FormWorkspace.js.map +1 -1
- package/dist/workspace/session-adapter.d.ts +95 -0
- package/dist/workspace/session-adapter.d.ts.map +1 -0
- package/dist/workspace/session-adapter.js +29 -0
- package/dist/workspace/session-adapter.js.map +1 -0
- package/package.json +10 -5
- package/styles/source.css +9 -0
|
@@ -1,29 +1,166 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
/**
|
|
3
3
|
* useChiefTurn — the Chief turn flow, extracted from the rail panel so every
|
|
4
|
-
* PLACEMENT of the AI Region (rail,
|
|
5
|
-
* turn lifecycle over the injected TurnAdapter (P1-1 — the product
|
|
6
|
-
* wire), the conductor-sdk event vocabulary (`delta` / `routed` /
|
|
7
|
-
* the reference outcome precedence (routed > error > streamed
|
|
8
|
-
* in-console routed-directive guard DERIVED from the Flavor's
|
|
9
|
-
* moves the panel; it never touches this hook.
|
|
4
|
+
* PLACEMENT of the AI Region (rail, push, overlay) runs the IDENTICAL
|
|
5
|
+
* behavior: turn lifecycle over the injected TurnAdapter (P1-1 — the product
|
|
6
|
+
* owns the wire), the conductor-sdk event vocabulary (`delta` / `routed` /
|
|
7
|
+
* `error`), the reference outcome precedence (routed > error > streamed
|
|
8
|
+
* text), and the in-console routed-directive guard DERIVED from the Flavor's
|
|
9
|
+
* nav. Placement moves the panel; it never touches this hook.
|
|
10
|
+
*
|
|
11
|
+
* `submitPrompt` is the one entry to a turn — the composer's form submit and
|
|
12
|
+
* the empty-state suggestion chips both call it, so a suggestion is a canned
|
|
13
|
+
* question, never a second code path.
|
|
10
14
|
*/
|
|
11
15
|
import * as React from "react";
|
|
12
|
-
import { useRouter } from "next/navigation";
|
|
16
|
+
import { useRouter, usePathname } from "next/navigation";
|
|
17
|
+
import { parseCanvasResult } from "@consciousclouds/ai-client";
|
|
13
18
|
import { flavorRoutes, isRouteInConsole } from "../nav/routes.js";
|
|
19
|
+
import { adoptConversationId, conversationStorageKey, getOrMintConversationId, subscribeConversationId, } from "./conversation-identity.js";
|
|
14
20
|
import { directiveToHref } from "./directive.js";
|
|
15
|
-
|
|
21
|
+
import { resolveChiefTitle, } from "./personality-display.js";
|
|
22
|
+
/**
|
|
23
|
+
* Render a recorded outcome as the thread's status line — the SAME vocabulary
|
|
24
|
+
* the live turn used, so a hydrated turn and its live twin read alike.
|
|
25
|
+
*/
|
|
26
|
+
function metaFromOutcome(outcome) {
|
|
27
|
+
if (!outcome)
|
|
28
|
+
return null;
|
|
29
|
+
if (outcome.kind === "routed") {
|
|
30
|
+
return {
|
|
31
|
+
kind: "routed",
|
|
32
|
+
text: `routed → ${outcome.target ?? ""} · 0 tokens`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (outcome.kind === "error") {
|
|
36
|
+
return { kind: "error", text: outcome.code ?? "error" };
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Map the adapter's storage-agnostic TURNS (session-binder.md's read contract —
|
|
42
|
+
* the adapter folds whatever its store speaks into turns; the shell never sees
|
|
43
|
+
* records) to rendered thread entries, keyed by the canonical turn id — the
|
|
44
|
+
* durable transcript and the screen share one name.
|
|
45
|
+
*/
|
|
46
|
+
function entriesFromTurns(turns) {
|
|
47
|
+
return turns.map((t) => ({
|
|
48
|
+
key: t.turnId ?? crypto.randomUUID(),
|
|
49
|
+
...(t.turnId ? { turnId: t.turnId } : {}),
|
|
50
|
+
question: t.prompt,
|
|
51
|
+
answer: t.answer,
|
|
52
|
+
meta: metaFromOutcome(t.outcome),
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
export function useChiefTurn({ flavor, adapter, personalityDisplay, context: providedContext, onComposed, onComposeActivity, conversationAdapter, }) {
|
|
16
56
|
const router = useRouter();
|
|
57
|
+
// WHERE the operator is — the one piece of operational context the shell owns
|
|
58
|
+
// outright (it owns the frame, so it knows the route). Filled on every turn so
|
|
59
|
+
// Chief can resolve "this"/"here" to the operator's actual location; a route
|
|
60
|
+
// change re-renders the hook, so submitPrompt always closes over the current path.
|
|
61
|
+
const pathname = usePathname();
|
|
62
|
+
// The conversation identity — MINT-OR-ADOPT (Session Binder slice 2a): restored
|
|
63
|
+
// from storage (a hard refresh continues the thread), minted-and-persisted when
|
|
64
|
+
// none exists, and replaced when the shell adopts a kept Workspace Session's
|
|
65
|
+
// owning conversation. It rides EVERY turn as the wire's `conversation_id` (the
|
|
66
|
+
// durable thread-grouping key) and composition provenance. A correlation
|
|
67
|
+
// reference only — never authority (reads re-authorize server-side).
|
|
68
|
+
const conversationKey = conversationStorageKey(flavor.chief?.personalityRef);
|
|
69
|
+
const [conversationId, setConversationId] = React.useState(() => getOrMintConversationId(conversationKey));
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
// Client-side reconcile (the SSR pass has no identity) + live adoptions.
|
|
72
|
+
setConversationId((current) => current || getOrMintConversationId(conversationKey));
|
|
73
|
+
return subscribeConversationId(conversationKey, setConversationId);
|
|
74
|
+
}, [conversationKey]);
|
|
17
75
|
const [cmd, setCmd] = React.useState("");
|
|
18
76
|
const [busy, setBusy] = React.useState(false);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const [
|
|
22
|
-
|
|
77
|
+
// The visible THREAD (slice 2b): speaking APPENDS an entry — it never erases
|
|
78
|
+
// the prior exchange. The last entry is the live turn while streaming.
|
|
79
|
+
const [turns, setTurns] = React.useState([]);
|
|
80
|
+
// The ACTIVE conversation as a ref (read inside async turn flows without a
|
|
81
|
+
// stale closure) and the in-flight turn's handle (aborted on adoption).
|
|
82
|
+
const activeConversationRef = React.useRef(conversationId);
|
|
83
|
+
const inflightRef = React.useRef(null);
|
|
84
|
+
// Switching conversations (adoption) clears the visible thread: those turns
|
|
85
|
+
// belong to the previous conversation — rendering them under the adopted one
|
|
86
|
+
// would cross-attribute the transcript. An IN-FLIGHT turn is aborted, not
|
|
87
|
+
// orphaned: its entry is gone, so letting the stream run would render nowhere
|
|
88
|
+
// while holding `busy` — and its composed canvas must never clobber the
|
|
89
|
+
// just-opened workspace (the switched-conversation guard in submitPrompt).
|
|
90
|
+
// (The adopted conversation's own history re-hydrates via the effect below.)
|
|
91
|
+
const prevConversationRef = React.useRef(conversationId);
|
|
92
|
+
React.useEffect(() => {
|
|
93
|
+
activeConversationRef.current = conversationId;
|
|
94
|
+
const prev = prevConversationRef.current;
|
|
95
|
+
prevConversationRef.current = conversationId;
|
|
96
|
+
if (prev && conversationId && prev !== conversationId) {
|
|
97
|
+
inflightRef.current?.abort();
|
|
98
|
+
setTurns([]);
|
|
99
|
+
}
|
|
100
|
+
}, [conversationId]);
|
|
101
|
+
// ── The durable transcript (ConversationAdapter, fail-closed slot) ─────────────────────────────
|
|
102
|
+
// The operator's threads, for the Conversations tab. Chrome: a failed list stays empty, quietly.
|
|
103
|
+
const [conversations, setConversations] = React.useState([]);
|
|
104
|
+
const refreshConversations = React.useCallback(async () => {
|
|
105
|
+
if (!conversationAdapter)
|
|
106
|
+
return;
|
|
107
|
+
try {
|
|
108
|
+
setConversations(await conversationAdapter.listConversations());
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
/* the list is chrome; the turn flow never depends on it */
|
|
112
|
+
}
|
|
113
|
+
}, [conversationAdapter]);
|
|
114
|
+
React.useEffect(() => {
|
|
115
|
+
void refreshConversations();
|
|
116
|
+
}, [refreshConversations]);
|
|
117
|
+
// HYDRATE the visible thread from the durable transcript when a conversation becomes active
|
|
118
|
+
// (mount restores the persisted thread — Chief's memory made VISIBLE; adoption swaps to the
|
|
119
|
+
// adopted thread's history). Guarded: a stale response for a previously-active conversation is
|
|
120
|
+
// discarded, and history MERGES BENEATH any live turn submitted while the read was in flight —
|
|
121
|
+
// the on-screen exchange survives, the history still appears.
|
|
122
|
+
React.useEffect(() => {
|
|
123
|
+
if (!conversationAdapter || !conversationId)
|
|
124
|
+
return;
|
|
125
|
+
let cancelled = false;
|
|
126
|
+
void (async () => {
|
|
127
|
+
try {
|
|
128
|
+
const turnsRead = await conversationAdapter.listTurns(conversationId);
|
|
129
|
+
if (cancelled || activeConversationRef.current !== conversationId)
|
|
130
|
+
return;
|
|
131
|
+
const hydrated = entriesFromTurns(turnsRead);
|
|
132
|
+
if (hydrated.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
// MERGE, never replace: a live turn submitted while this read was in
|
|
135
|
+
// flight always survives, and the fetched history still lands BEFORE it
|
|
136
|
+
// (the durable transcript cannot contain a turn that started after the
|
|
137
|
+
// read began). Entries already on screen (canonical turn id) are kept.
|
|
138
|
+
setTurns((current) => {
|
|
139
|
+
if (current.length === 0)
|
|
140
|
+
return hydrated;
|
|
141
|
+
const seen = new Set(current.map((t) => t.turnId).filter(Boolean));
|
|
142
|
+
const missing = hydrated.filter((h) => !h.turnId || !seen.has(h.turnId));
|
|
143
|
+
return missing.length > 0 ? [...missing, ...current] : current;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
/* hydration is chrome; the live flow works without it */
|
|
148
|
+
}
|
|
149
|
+
})();
|
|
150
|
+
return () => {
|
|
151
|
+
cancelled = true;
|
|
152
|
+
};
|
|
153
|
+
}, [conversationAdapter, conversationId]);
|
|
154
|
+
// Thread gestures — both are ADOPTIONS through the one identity seam, so every
|
|
155
|
+
// hook instance (and tab) converges on the same active conversation.
|
|
156
|
+
const openConversation = React.useCallback((id) => adoptConversationId(conversationKey, id), [conversationKey]);
|
|
157
|
+
const newConversation = React.useCallback(() => adoptConversationId(conversationKey, crypto.randomUUID()), [conversationKey]);
|
|
158
|
+
const title = resolveChiefTitle(personalityDisplay, flavor.chief?.title);
|
|
23
159
|
const placeholder = flavor.chief?.placeholder ?? `Ask ${title}…`;
|
|
24
160
|
const hint = flavor.chief?.hint ??
|
|
25
161
|
`Ask ${title} about this console — it reasons over the granted knowledge envelope`;
|
|
26
162
|
const hintExample = flavor.chief?.hintExample;
|
|
163
|
+
const suggestions = flavor.chief?.suggestions ?? [];
|
|
27
164
|
/**
|
|
28
165
|
* The set of routes this console actually serves — DERIVED from the
|
|
29
166
|
* Flavor's nav. Chief's deterministic router can emit a navigation to
|
|
@@ -32,27 +169,63 @@ export function useChiefTurn({ flavor, adapter }) {
|
|
|
32
169
|
* than land the operator on a 404.
|
|
33
170
|
*/
|
|
34
171
|
const routes = React.useMemo(() => flavorRoutes(flavor.nav), [flavor.nav]);
|
|
35
|
-
async function
|
|
36
|
-
|
|
37
|
-
const prompt = cmd.trim();
|
|
172
|
+
async function submitPrompt(rawPrompt) {
|
|
173
|
+
const prompt = rawPrompt.trim();
|
|
38
174
|
if (!prompt || busy)
|
|
39
175
|
return;
|
|
40
176
|
setBusy(true);
|
|
41
177
|
setCmd("");
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
178
|
+
// APPEND this exchange to the thread (never replace) and patch it in place
|
|
179
|
+
// as the stream progresses — keyed so a stale closure can never touch a
|
|
180
|
+
// different entry.
|
|
181
|
+
const entryKey = crypto.randomUUID();
|
|
182
|
+
setTurns((ts) => [
|
|
183
|
+
...ts,
|
|
184
|
+
{ key: entryKey, question: prompt, answer: "", meta: null },
|
|
185
|
+
]);
|
|
186
|
+
const patchEntry = (patch) => setTurns((ts) => ts.map((t) => (t.key === entryKey ? patch(t) : t)));
|
|
45
187
|
// Collected while streaming; resolved after the stream ends with the
|
|
46
188
|
// reference panel's outcome precedence: routed > error > streamed text.
|
|
189
|
+
// `composed` is ADDITIVE, not exclusive: a turn may both narrate (text) AND
|
|
190
|
+
// compose a workspace, so it rides alongside the answer — only a `routed`
|
|
191
|
+
// (navigation) turn suppresses it. Last canvas-definition in the turn wins
|
|
192
|
+
// (the final composed workspace is the active one).
|
|
47
193
|
const outcome = {
|
|
48
194
|
routed: null,
|
|
49
195
|
error: null,
|
|
196
|
+
composed: null,
|
|
197
|
+
composer: null,
|
|
198
|
+
turnId: null,
|
|
50
199
|
};
|
|
200
|
+
// Operational context forwarded with the turn: the shell-owned location,
|
|
201
|
+
// plus whatever scope/engagement the product supplied. Inert and advisory —
|
|
202
|
+
// the personality reads it for situational awareness; it is never authority.
|
|
203
|
+
const context = { location: pathname, ...providedContext };
|
|
51
204
|
try {
|
|
52
|
-
const handle = adapter.startTurn(
|
|
53
|
-
|
|
205
|
+
const handle = adapter.startTurn(
|
|
206
|
+
// conversationId rides every turn (slice 2a): the adapter forwards it
|
|
207
|
+
// verbatim as the wire's `conversation_id`, so the conductor records
|
|
208
|
+
// this turn's transcript under the thread the shell is rendering.
|
|
209
|
+
{
|
|
210
|
+
prompt,
|
|
211
|
+
personalityRef: flavor.chief?.personalityRef,
|
|
212
|
+
context,
|
|
213
|
+
conversationId,
|
|
214
|
+
},
|
|
215
|
+
// (the handle is retained below so a conversation switch can abort it)
|
|
216
|
+
(name, data) => {
|
|
217
|
+
if (name === "started") {
|
|
218
|
+
// The canonical runtime turn id — the ONLY id a workspace anchor may carry
|
|
219
|
+
// (a frontend-fabricated id would never match the durable transcript).
|
|
220
|
+
outcome.turnId = data.turn_id;
|
|
221
|
+
patchEntry((t) => ({
|
|
222
|
+
...t,
|
|
223
|
+
turnId: data.turn_id,
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
else if (name === "delta") {
|
|
54
227
|
const { text } = data;
|
|
55
|
-
|
|
228
|
+
patchEntry((t) => ({ ...t, answer: t.answer + text }));
|
|
56
229
|
}
|
|
57
230
|
else if (name === "routed") {
|
|
58
231
|
outcome.routed = data;
|
|
@@ -60,40 +233,123 @@ export function useChiefTurn({ flavor, adapter }) {
|
|
|
60
233
|
else if (name === "error") {
|
|
61
234
|
outcome.error = data.message;
|
|
62
235
|
}
|
|
236
|
+
else if (name === "tool_start") {
|
|
237
|
+
// A compose-class tool began — the center may start its composing motion NOW,
|
|
238
|
+
// synchronized to the actual execution (never a fake spinner).
|
|
239
|
+
const tool = data.tool ?? "";
|
|
240
|
+
if (tool.startsWith("compose"))
|
|
241
|
+
onComposeActivity?.("composing");
|
|
242
|
+
}
|
|
243
|
+
else if (name === "tool_done") {
|
|
244
|
+
// The compose channel: a tool finished — parse its result through the ONE
|
|
245
|
+
// provider-blind ai-client parser (never hand-roll JSON.parse here). Only a
|
|
246
|
+
// `canvas-definition` becomes a composed workspace; anything else is ordinary
|
|
247
|
+
// data and is ignored by this seam. Last one in the turn wins.
|
|
248
|
+
const toolDone = data;
|
|
249
|
+
const parsed = parseCanvasResult(toolDone.result);
|
|
250
|
+
if (parsed?.kind === "canvas-definition") {
|
|
251
|
+
outcome.composed = parsed.canvas;
|
|
252
|
+
outcome.composer = toolDone.tool ?? null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
63
255
|
});
|
|
256
|
+
inflightRef.current = handle;
|
|
64
257
|
await handle.done;
|
|
65
258
|
}
|
|
66
259
|
catch (err) {
|
|
67
260
|
outcome.error = err instanceof Error ? err.message : String(err);
|
|
68
261
|
}
|
|
262
|
+
finally {
|
|
263
|
+
inflightRef.current = null;
|
|
264
|
+
}
|
|
265
|
+
// The conversation switched (a kept session was adopted) while this turn
|
|
266
|
+
// streamed: the turn was aborted and its entry cleared. Its late results
|
|
267
|
+
// belong to the PREVIOUS thread — patch nothing, compose nothing (the
|
|
268
|
+
// operator's just-opened workspace must not be clobbered by a stale canvas).
|
|
269
|
+
const conversationSwitched = activeConversationRef.current !== conversationId;
|
|
270
|
+
if (conversationSwitched) {
|
|
271
|
+
onComposeActivity?.("idle");
|
|
272
|
+
setBusy(false);
|
|
273
|
+
// The aborted turn's group is still durably written (the conductor writes
|
|
274
|
+
// on a background context) — the threads list must reflect it.
|
|
275
|
+
void refreshConversations();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
69
278
|
if (outcome.routed) {
|
|
70
279
|
const href = directiveToHref(outcome.routed);
|
|
71
280
|
if (isRouteInConsole(routes, href)) {
|
|
72
|
-
|
|
281
|
+
patchEntry((t) => ({
|
|
282
|
+
...t,
|
|
283
|
+
meta: { kind: "routed", text: `routed → ${href} · 0 tokens` },
|
|
284
|
+
}));
|
|
73
285
|
router.push(href);
|
|
74
286
|
}
|
|
75
287
|
else {
|
|
76
|
-
|
|
288
|
+
patchEntry((t) => ({
|
|
289
|
+
...t,
|
|
290
|
+
meta: {
|
|
291
|
+
kind: "unavailable",
|
|
292
|
+
text: `${href} isn't part of this console yet`,
|
|
293
|
+
},
|
|
294
|
+
}));
|
|
77
295
|
}
|
|
78
296
|
}
|
|
79
297
|
else if (outcome.error) {
|
|
80
|
-
|
|
298
|
+
const message = outcome.error;
|
|
299
|
+
patchEntry((t) => ({ ...t, meta: { kind: "error", text: message } }));
|
|
300
|
+
}
|
|
301
|
+
// Lift a composed workspace to the shell (which mounts it in the CENTER via
|
|
302
|
+
// WorkspaceHost). Additive to the text answer; a routed turn never composes.
|
|
303
|
+
// Fired after the stream so a single, final canvas reaches the center per turn.
|
|
304
|
+
// Provenance rides along: composer = the tool, intent = the emitter's
|
|
305
|
+
// correlationId, conversationId = this Chief session — the source state the
|
|
306
|
+
// Keep-composition gesture persists.
|
|
307
|
+
if (!outcome.composed || outcome.routed) {
|
|
308
|
+
// The turn ended without a composition (or navigated) — clear any composing motion.
|
|
309
|
+
onComposeActivity?.("idle");
|
|
310
|
+
}
|
|
311
|
+
if (outcome.composed && !outcome.routed) {
|
|
312
|
+
onComposed?.(outcome.composed, {
|
|
313
|
+
composer: outcome.composer ?? "unknown",
|
|
314
|
+
intent: outcome.composed.correlationId ?? "",
|
|
315
|
+
conversationId,
|
|
316
|
+
...(outcome.turnId ? { turnId: outcome.turnId } : {}),
|
|
317
|
+
});
|
|
81
318
|
}
|
|
82
319
|
setBusy(false);
|
|
320
|
+
// The turn just landed in the durable transcript — refresh the threads list
|
|
321
|
+
// so a first-turn conversation appears (and recency reorders) without a reload.
|
|
322
|
+
void refreshConversations();
|
|
323
|
+
}
|
|
324
|
+
async function submit(e) {
|
|
325
|
+
e.preventDefault();
|
|
326
|
+
await submitPrompt(cmd);
|
|
83
327
|
}
|
|
328
|
+
// The current turn is the thread's LAST entry — question/answer/meta stay on
|
|
329
|
+
// the session contract as its projection, so presentations that only render
|
|
330
|
+
// the live exchange keep working unchanged.
|
|
331
|
+
const last = turns.length > 0 ? turns[turns.length - 1] : undefined;
|
|
84
332
|
return {
|
|
85
333
|
title,
|
|
86
334
|
placeholder,
|
|
87
335
|
hint,
|
|
88
336
|
hintExample,
|
|
337
|
+
suggestions,
|
|
89
338
|
cmd,
|
|
90
339
|
setCmd,
|
|
91
340
|
busy,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
341
|
+
turns,
|
|
342
|
+
question: last?.question ?? null,
|
|
343
|
+
answer: last?.answer ?? "",
|
|
344
|
+
meta: last?.meta ?? null,
|
|
345
|
+
showThread: turns.length > 0,
|
|
96
346
|
submit,
|
|
347
|
+
submitPrompt,
|
|
348
|
+
transcriptEnabled: Boolean(conversationAdapter),
|
|
349
|
+
conversations,
|
|
350
|
+
activeConversationId: conversationId,
|
|
351
|
+
openConversation,
|
|
352
|
+
newConversation,
|
|
97
353
|
};
|
|
98
354
|
}
|
|
99
355
|
//# sourceMappingURL=use-chief-turn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chief-turn.js","sourceRoot":"","sources":["../../src/chief/use-chief-turn.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-chief-turn.js","sourceRoot":"","sources":["../../src/chief/use-chief-turn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-controlled-open.d.ts","sourceRoot":"","sources":["../../src/chief/use-controlled-open.ts"],"names":[],"mappings":"AAYA,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,OAAO,GAAG,SAAS,EAC7B,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,EACnD,WAAW,UAAQ,GAClB,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,CAkBpC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* useControlledOpen — the Chief presentations' controlled/uncontrolled open
|
|
4
|
+
* state (the standard React pattern): inside `OperatorShell` the frame owns
|
|
5
|
+
* the state (its header trigger and the panel drive the SAME value); mounted
|
|
6
|
+
* standalone, the presentation self-manages. `onOpenChange` fires either
|
|
7
|
+
* way, so a composition root can observe without controlling.
|
|
8
|
+
*/
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
export function useControlledOpen(openProp, onOpenChange, defaultOpen = false) {
|
|
11
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
|
|
12
|
+
const controlled = openProp !== undefined;
|
|
13
|
+
const open = controlled ? openProp : internalOpen;
|
|
14
|
+
// Keep the callback identity stable across renders without re-subscribing
|
|
15
|
+
// the listeners that close over it.
|
|
16
|
+
const onOpenChangeRef = React.useRef(onOpenChange);
|
|
17
|
+
onOpenChangeRef.current = onOpenChange;
|
|
18
|
+
const controlledRef = React.useRef(controlled);
|
|
19
|
+
controlledRef.current = controlled;
|
|
20
|
+
const setOpen = React.useCallback((next) => {
|
|
21
|
+
if (!controlledRef.current)
|
|
22
|
+
setInternalOpen(next);
|
|
23
|
+
onOpenChangeRef.current?.(next);
|
|
24
|
+
}, []);
|
|
25
|
+
return [open, setOpen];
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=use-controlled-open.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-controlled-open.js","sourceRoot":"","sources":["../../src/chief/use-controlled-open.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,UAAU,iBAAiB,CAC/B,QAA6B,EAC7B,YAAmD,EACnD,WAAW,GAAG,KAAK;IAEnB,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,QAAQ,KAAK,SAAS,CAAC;IAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;IAElD,0EAA0E;IAC1E,oCAAoC;IACpC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACnD,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;IACvC,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC/C,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC;IAEnC,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,IAAa,EAAE,EAAE;QAClD,IAAI,CAAC,aAAa,CAAC,OAAO;YAAE,eAAe,CAAC,IAAI,CAAC,CAAC;QAClD,eAAe,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SmoothStreamOptions {
|
|
2
|
+
/** The source is still delivering text (a turn in flight). */
|
|
3
|
+
streaming: boolean;
|
|
4
|
+
/** prefers-reduced-motion → reveal instantly, no typewriter. */
|
|
5
|
+
reduced: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function useSmoothStream(target: string, { streaming, reduced }: SmoothStreamOptions): string;
|
|
8
|
+
//# sourceMappingURL=use-smooth-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-smooth-stream.d.ts","sourceRoot":"","sources":["../../src/chief/use-smooth-stream.ts"],"names":[],"mappings":"AAkCA,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;CAClB;AAcD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,mBAAmB,GAAG,MAAM,CAuFnG"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* useSmoothStream — decouple the VISUAL reveal of streamed text from the
|
|
4
|
+
* irregular pace at which the network delivers it. This is what makes a chat
|
|
5
|
+
* answer FLOW instead of CHUNK.
|
|
6
|
+
*
|
|
7
|
+
* The problem: SSE deltas arrive in bursts (1 token, then 12, then a pause,
|
|
8
|
+
* then 5). Rendering each delta the instant it lands paints those bursts onto
|
|
9
|
+
* the screen — the "chunking" you feel. Every polished chat UI (ChatGPT,
|
|
10
|
+
* Claude, the Vercel AI SDK) fixes it the same way: BUFFER the incoming text
|
|
11
|
+
* and DRAIN it to the screen at a smooth, adaptive pace. The network bursts;
|
|
12
|
+
* the screen flows.
|
|
13
|
+
*
|
|
14
|
+
* This hook is that drain. Given the full received text (`target`, which the
|
|
15
|
+
* turn hook grows delta-by-delta), it returns a `displayed` PREFIX that catches
|
|
16
|
+
* up to `target` a little each animation frame (requestAnimationFrame). The
|
|
17
|
+
* catch-up is framerate-independent (exponential approach) and ADAPTIVE: far
|
|
18
|
+
* behind (a fast model, a big chunk) → big steps so it never visibly lags; near
|
|
19
|
+
* caught-up → small steps so it reads like calm typing; and once the stream
|
|
20
|
+
* ENDS, the tail drains faster so the reveal doesn't linger after the answer.
|
|
21
|
+
*
|
|
22
|
+
* Presentation only — it never touches the turn state, the adapter, or the
|
|
23
|
+
* transport; the deltas still arrive exactly as they did. Respects
|
|
24
|
+
* prefers-reduced-motion (`reduced` → `displayed === target` instantly, no
|
|
25
|
+
* typewriter), and snaps cleanly when a new turn resets `target` shorter.
|
|
26
|
+
*
|
|
27
|
+
* Reusable-shaped: a paced reveal is not Chief-specific — it is a candidate to
|
|
28
|
+
* GRADUATE into @consciousclouds/ui once a second surface streams text. Kept
|
|
29
|
+
* here for now (Project Zero refinement, not adopter work).
|
|
30
|
+
*/
|
|
31
|
+
import * as React from "react";
|
|
32
|
+
/** Exponential approach rate (per second) toward the target length — higher is
|
|
33
|
+
* snappier. Faster once the stream ENDS so the tail finishes promptly. Tune
|
|
34
|
+
* these two to trade "calm typing" against "keeps up": lower = more typewriter. */
|
|
35
|
+
const APPROACH_STREAMING = 6;
|
|
36
|
+
const APPROACH_DONE = 16;
|
|
37
|
+
/** The calm base rate (chars/sec) — the floor the reveal holds when caught up,
|
|
38
|
+
* so it reads like steady typing (~26 cps ≈ 38ms/char) rather than rushing.
|
|
39
|
+
* Calibrated to the "fluid, not fast" end of the reference libraries. */
|
|
40
|
+
const MIN_CHARS_PER_SEC = 26;
|
|
41
|
+
/** Clamp a single frame's delta (e.g. after a background tab) so we never leap. */
|
|
42
|
+
const MAX_FRAME_SEC = 1 / 20;
|
|
43
|
+
export function useSmoothStream(target, { streaming, reduced }) {
|
|
44
|
+
const [len, setLen] = React.useState(0);
|
|
45
|
+
const targetRef = React.useRef(target);
|
|
46
|
+
targetRef.current = target;
|
|
47
|
+
const streamingRef = React.useRef(streaming);
|
|
48
|
+
streamingRef.current = streaming;
|
|
49
|
+
const lenRef = React.useRef(0);
|
|
50
|
+
lenRef.current = len;
|
|
51
|
+
const rafRef = React.useRef(0);
|
|
52
|
+
const lastTsRef = React.useRef(0);
|
|
53
|
+
const stop = React.useCallback(() => {
|
|
54
|
+
if (rafRef.current)
|
|
55
|
+
cancelAnimationFrame(rafRef.current);
|
|
56
|
+
rafRef.current = 0;
|
|
57
|
+
lastTsRef.current = 0;
|
|
58
|
+
}, []);
|
|
59
|
+
const tick = React.useCallback((ts) => {
|
|
60
|
+
if (lastTsRef.current === 0)
|
|
61
|
+
lastTsRef.current = ts;
|
|
62
|
+
const dt = Math.min((ts - lastTsRef.current) / 1000, MAX_FRAME_SEC);
|
|
63
|
+
lastTsRef.current = ts;
|
|
64
|
+
const text = targetRef.current;
|
|
65
|
+
const targetLen = text.length;
|
|
66
|
+
const remaining = targetLen - lenRef.current;
|
|
67
|
+
if (remaining > 0) {
|
|
68
|
+
const approach = streamingRef.current ? APPROACH_STREAMING : APPROACH_DONE;
|
|
69
|
+
const eased = Math.ceil(remaining * (1 - Math.exp(-dt * approach)));
|
|
70
|
+
const floor = Math.ceil(MIN_CHARS_PER_SEC * dt);
|
|
71
|
+
let next = Math.min(targetLen, lenRef.current + Math.max(eased, floor, 1));
|
|
72
|
+
// Reveal WHOLE words while more text is still coming, so each word lands
|
|
73
|
+
// complete and can fade in as a unit (the "materializing" feel). Snap back
|
|
74
|
+
// to the last word boundary in (cur, next]; inside a long unbroken word,
|
|
75
|
+
// fall through to the char position so a long token never stalls the reveal.
|
|
76
|
+
if (next < targetLen) {
|
|
77
|
+
for (let i = next; i > lenRef.current; i--) {
|
|
78
|
+
if (/\s/.test(text[i - 1])) {
|
|
79
|
+
next = i;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
lenRef.current = next;
|
|
85
|
+
setLen(next);
|
|
86
|
+
}
|
|
87
|
+
// Keep draining while the stream is live (more may arrive) or a backlog
|
|
88
|
+
// remains; otherwise let the loop go idle until the next kick.
|
|
89
|
+
if (streamingRef.current || lenRef.current < targetRef.current.length) {
|
|
90
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
rafRef.current = 0;
|
|
94
|
+
lastTsRef.current = 0;
|
|
95
|
+
}
|
|
96
|
+
}, []);
|
|
97
|
+
const kick = React.useCallback(() => {
|
|
98
|
+
if (!rafRef.current)
|
|
99
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
100
|
+
}, [tick]);
|
|
101
|
+
// Reduced motion: mirror the target exactly (no typewriter), and stop any loop.
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
if (!reduced)
|
|
104
|
+
return;
|
|
105
|
+
stop();
|
|
106
|
+
if (lenRef.current !== target.length)
|
|
107
|
+
setLen(target.length);
|
|
108
|
+
}, [reduced, target, stop]);
|
|
109
|
+
// A new turn resets `target` shorter than what we've shown → snap back so the
|
|
110
|
+
// previous answer never lingers under the new question.
|
|
111
|
+
React.useEffect(() => {
|
|
112
|
+
if (target.length < lenRef.current) {
|
|
113
|
+
lenRef.current = target.length;
|
|
114
|
+
setLen(target.length);
|
|
115
|
+
}
|
|
116
|
+
}, [target]);
|
|
117
|
+
// Kick the drain whenever there's work and motion is allowed. `kick` is a
|
|
118
|
+
// no-op if the loop is already running, so a delta storm doesn't restart it.
|
|
119
|
+
React.useEffect(() => {
|
|
120
|
+
if (reduced)
|
|
121
|
+
return;
|
|
122
|
+
if (streaming || lenRef.current < targetRef.current.length)
|
|
123
|
+
kick();
|
|
124
|
+
}, [target, streaming, reduced, kick]);
|
|
125
|
+
React.useEffect(() => stop, [stop]);
|
|
126
|
+
return reduced ? target : target.slice(0, len);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=use-smooth-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-smooth-stream.js","sourceRoot":"","sources":["../../src/chief/use-smooth-stream.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B;;oFAEoF;AACpF,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB;;0EAE0E;AAC1E,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,mFAAmF;AACnF,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,CAAC;AAE7B,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,EAAE,SAAS,EAAE,OAAO,EAAuB;IACzF,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAExC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7C,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;IACjC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC;IACrB,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAElC,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAClC,IAAI,MAAM,CAAC,OAAO;YAAE,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;QACnB,SAAS,CAAC,OAAO,GAAG,CAAC,CAAC;IACxB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,EAAU,EAAE,EAAE;QAC5C,IAAI,SAAS,CAAC,OAAO,KAAK,CAAC;YAAE,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC;QACpD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC,CAAC;QACpE,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC;QAEvB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC;QAC9B,MAAM,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC;QAC7C,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,aAAa,CAAC;YAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACpE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC;YAChD,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YAC3E,yEAAyE;YACzE,2EAA2E;YAC3E,yEAAyE;YACzE,6EAA6E;YAC7E,IAAI,IAAI,GAAG,SAAS,EAAE,CAAC;gBACrB,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC3C,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC3B,IAAI,GAAG,CAAC,CAAC;wBACT,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;YACD,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;QAED,wEAAwE;QACxE,+DAA+D;QAC/D,IAAI,YAAY,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACtE,MAAM,CAAC,OAAO,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;YACnB,SAAS,CAAC,OAAO,GAAG,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAClC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,CAAC,OAAO,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,gFAAgF;IAChF,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,EAAE,CAAC;QACP,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,MAAM;YAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAE5B,8EAA8E;IAC9E,wDAAwD;IACxD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,0EAA0E;IAC1E,6EAA6E;IAC7E,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,OAAO;YAAE,OAAO;QACpB,IAAI,SAAS,IAAI,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM;YAAE,IAAI,EAAE,CAAC;IACrE,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAEvC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEpC,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC"}
|