@xenide-io/the-old-ui-theme 0.6.9 → 0.6.11
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/suite.d.mts +28 -2
- package/dist/suite.d.ts +28 -2
- package/dist/suite.js +195 -30
- package/dist/suite.mjs +193 -36
- package/package.json +1 -1
- package/src/suite/components/ai-panel.test.ts +43 -0
- package/src/suite/components/ai-panel.tsx +192 -14
- package/src/suite/components/suite-mobile-drawer.test.tsx +33 -0
- package/src/suite/components/suite-mobile-drawer.tsx +9 -2
- package/src/suite/components/suite-sidebar.tsx +22 -14
- package/src/suite/index.ts +10 -0
|
@@ -31,11 +31,72 @@ import {
|
|
|
31
31
|
const SuiteAiBlockNoteMessage = lazy(() => import("./ai-message-blocknote"));
|
|
32
32
|
|
|
33
33
|
export const SUITE_OPEN_ASK_AI_EVENT = "shellstack:open-ask-ai";
|
|
34
|
+
export const SUITE_ASK_AI_OPEN_KEY = "shellstack:shelly-open";
|
|
35
|
+
export const SUITE_ASK_AI_OPEN_QUERY = "shelly";
|
|
36
|
+
export const SUITE_MUTATED_EVENT = "shellstack:suite-mutated";
|
|
37
|
+
export const TURTLETIME_TIMER_MUTATED_EVENT = "tt-timer-mutated";
|
|
34
38
|
|
|
35
39
|
export type SuiteAskAiOpenDetail = {
|
|
36
40
|
prompt?: string;
|
|
37
41
|
};
|
|
38
42
|
|
|
43
|
+
export function persistSuiteAskAiOpen(open: boolean): void {
|
|
44
|
+
if (typeof window === "undefined") return;
|
|
45
|
+
try {
|
|
46
|
+
if (open) sessionStorage.setItem(SUITE_ASK_AI_OPEN_KEY, "1");
|
|
47
|
+
else sessionStorage.removeItem(SUITE_ASK_AI_OPEN_KEY);
|
|
48
|
+
} catch {
|
|
49
|
+
// private mode / blocked storage
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function readSuiteAskAiOpen(): boolean {
|
|
54
|
+
if (typeof window === "undefined") return false;
|
|
55
|
+
try {
|
|
56
|
+
return sessionStorage.getItem(SUITE_ASK_AI_OPEN_KEY) === "1";
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Stamp a destination path so the other app opens Shelly after SSO. */
|
|
63
|
+
export function pathWithSuiteAskAiOpen(path: string): string {
|
|
64
|
+
const url = new URL((path || "/").trim() || "/", "http://local.invalid");
|
|
65
|
+
url.searchParams.set(SUITE_ASK_AI_OPEN_QUERY, "1");
|
|
66
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Open Shelly only after a `?shelly=1` handoff — not a leftover session flag. */
|
|
70
|
+
export function consumeSuiteAskAiOpenFromLocation(): boolean {
|
|
71
|
+
if (typeof window === "undefined") return false;
|
|
72
|
+
const url = new URL(window.location.href);
|
|
73
|
+
if (url.searchParams.get(SUITE_ASK_AI_OPEN_QUERY) !== "1") return false;
|
|
74
|
+
persistSuiteAskAiOpen(true);
|
|
75
|
+
url.searchParams.delete(SUITE_ASK_AI_OPEN_QUERY);
|
|
76
|
+
window.history.replaceState(
|
|
77
|
+
window.history.state,
|
|
78
|
+
"",
|
|
79
|
+
`${url.pathname}${url.search}${url.hash}`,
|
|
80
|
+
);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type SuiteAiActionStatus =
|
|
85
|
+
| "proposed"
|
|
86
|
+
| "applied"
|
|
87
|
+
| "cancelled"
|
|
88
|
+
| "failed";
|
|
89
|
+
|
|
90
|
+
export interface SuiteAiAction {
|
|
91
|
+
id: string;
|
|
92
|
+
kind: string;
|
|
93
|
+
status: SuiteAiActionStatus;
|
|
94
|
+
summary: string;
|
|
95
|
+
href?: string;
|
|
96
|
+
error?: string;
|
|
97
|
+
payload?: Record<string, unknown>;
|
|
98
|
+
}
|
|
99
|
+
|
|
39
100
|
let pendingAskAiPrompt: string | null = null;
|
|
40
101
|
|
|
41
102
|
/** Open the suite Ask AI panel from anywhere (Today card, command palette, …). */
|
|
@@ -65,6 +126,25 @@ export interface SuiteAiChatMessage {
|
|
|
65
126
|
role: "user" | "assistant";
|
|
66
127
|
content: string;
|
|
67
128
|
created_at?: string;
|
|
129
|
+
actions?: SuiteAiAction[];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function notifySuiteMutated(actions?: SuiteAiAction[]): void {
|
|
133
|
+
if (typeof window === "undefined" || !actions?.length) return;
|
|
134
|
+
const kinds = actions.map((action) => action.kind);
|
|
135
|
+
if (
|
|
136
|
+
kinds.some(
|
|
137
|
+
(kind) =>
|
|
138
|
+
kind.includes("timer") ||
|
|
139
|
+
kind.includes("time") ||
|
|
140
|
+
kind.includes("hours"),
|
|
141
|
+
)
|
|
142
|
+
) {
|
|
143
|
+
window.dispatchEvent(new Event(TURTLETIME_TIMER_MUTATED_EVENT));
|
|
144
|
+
}
|
|
145
|
+
window.dispatchEvent(
|
|
146
|
+
new CustomEvent(SUITE_MUTATED_EVENT, { detail: { kinds } }),
|
|
147
|
+
);
|
|
68
148
|
}
|
|
69
149
|
|
|
70
150
|
/**
|
|
@@ -84,6 +164,8 @@ export function SuiteAiPanel({
|
|
|
84
164
|
onOpenChange,
|
|
85
165
|
suiteAppBases,
|
|
86
166
|
onSwitchApp,
|
|
167
|
+
onSameAppNavigate,
|
|
168
|
+
resolveAction,
|
|
87
169
|
}: {
|
|
88
170
|
presets?: SuiteAiPreset[];
|
|
89
171
|
emptyState?: ReactNode;
|
|
@@ -105,12 +187,20 @@ export function SuiteAiPanel({
|
|
|
105
187
|
suiteAppBases?: Partial<Record<SuiteAppSlug, string>>;
|
|
106
188
|
/** Cross-app SSO. Same-app links just navigate to `path`. */
|
|
107
189
|
onSwitchApp?: (app: SuiteAppSlug, path: string) => void;
|
|
190
|
+
/** Client-side same-app route change — keeps Shelly mounted. */
|
|
191
|
+
onSameAppNavigate?: (path: string) => void;
|
|
192
|
+
resolveAction?: (
|
|
193
|
+
id: string,
|
|
194
|
+
decision: "confirm" | "cancel",
|
|
195
|
+
) => Promise<{ messages: SuiteAiChatMessage[] }>;
|
|
108
196
|
}) {
|
|
109
197
|
const inputId = useId();
|
|
110
198
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
|
|
199
|
+
const [resolvingId, setResolvingId] = useState("");
|
|
111
200
|
const open = openProp ?? uncontrolledOpen;
|
|
112
201
|
const setOpen = useCallback(
|
|
113
202
|
(next: boolean) => {
|
|
203
|
+
persistSuiteAskAiOpen(next);
|
|
114
204
|
if (openProp === undefined) setUncontrolledOpen(next);
|
|
115
205
|
onOpenChange?.(next);
|
|
116
206
|
},
|
|
@@ -215,7 +305,11 @@ export function SuiteAiPanel({
|
|
|
215
305
|
signal: controller.signal,
|
|
216
306
|
});
|
|
217
307
|
if (controller.signal.aborted) return;
|
|
218
|
-
|
|
308
|
+
const nextMessages = result.messages ?? [];
|
|
309
|
+
setMessages(nextMessages);
|
|
310
|
+
notifySuiteMutated(
|
|
311
|
+
nextMessages.flatMap((message) => message.actions ?? []),
|
|
312
|
+
);
|
|
219
313
|
} catch (err) {
|
|
220
314
|
if (controller.signal.aborted) return;
|
|
221
315
|
setMessages((prev) =>
|
|
@@ -264,6 +358,29 @@ export function SuiteAiPanel({
|
|
|
264
358
|
}
|
|
265
359
|
}
|
|
266
360
|
|
|
361
|
+
async function handleResolve(
|
|
362
|
+
actionId: string,
|
|
363
|
+
decision: "confirm" | "cancel",
|
|
364
|
+
) {
|
|
365
|
+
if (!resolveAction || resolvingId) return;
|
|
366
|
+
setResolvingId(actionId);
|
|
367
|
+
setError("");
|
|
368
|
+
try {
|
|
369
|
+
const result = await resolveAction(actionId, decision);
|
|
370
|
+
const nextMessages = result.messages ?? [];
|
|
371
|
+
setMessages(nextMessages);
|
|
372
|
+
notifySuiteMutated(
|
|
373
|
+
nextMessages.flatMap((message) => message.actions ?? []),
|
|
374
|
+
);
|
|
375
|
+
} catch (err) {
|
|
376
|
+
setError(
|
|
377
|
+
err instanceof Error ? err.message : "Could not apply that action.",
|
|
378
|
+
);
|
|
379
|
+
} finally {
|
|
380
|
+
setResolvingId("");
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
267
384
|
function onConversationClick(event: ReactMouseEvent<HTMLDivElement>) {
|
|
268
385
|
if (event.defaultPrevented) return;
|
|
269
386
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
@@ -280,12 +397,15 @@ export function SuiteAiPanel({
|
|
|
280
397
|
);
|
|
281
398
|
if (!match) return;
|
|
282
399
|
event.preventDefault();
|
|
400
|
+
persistSuiteAskAiOpen(true);
|
|
283
401
|
if (match.sameApp) {
|
|
284
|
-
|
|
402
|
+
if (onSameAppNavigate) onSameAppNavigate(match.path);
|
|
403
|
+
else window.location.assign(pathWithSuiteAskAiOpen(match.path));
|
|
285
404
|
return;
|
|
286
405
|
}
|
|
287
|
-
|
|
288
|
-
|
|
406
|
+
const openPath = pathWithSuiteAskAiOpen(match.path);
|
|
407
|
+
if (onSwitchApp) onSwitchApp(match.slug, openPath);
|
|
408
|
+
else window.location.assign(new URL(openPath, href).toString());
|
|
289
409
|
}
|
|
290
410
|
|
|
291
411
|
if (!open) return null;
|
|
@@ -391,16 +511,74 @@ export function SuiteAiPanel({
|
|
|
391
511
|
>
|
|
392
512
|
<Icon className="h-3.5 w-3.5" />
|
|
393
513
|
</span>
|
|
394
|
-
<div className="min-w-0 max-w-[85%]
|
|
395
|
-
<
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
514
|
+
<div className="min-w-0 max-w-[85%] space-y-2">
|
|
515
|
+
<div className="rounded-2xl rounded-bl-md bg-ph-muted px-3 py-2 text-sm leading-relaxed text-ph-ink">
|
|
516
|
+
<Suspense
|
|
517
|
+
fallback={
|
|
518
|
+
<p className="whitespace-pre-wrap break-words">
|
|
519
|
+
{message.content}
|
|
520
|
+
</p>
|
|
521
|
+
}
|
|
522
|
+
>
|
|
523
|
+
<SuiteAiBlockNoteMessage markdown={message.content} />
|
|
524
|
+
</Suspense>
|
|
525
|
+
</div>
|
|
526
|
+
{(message.actions ?? []).map((action) => (
|
|
527
|
+
<div
|
|
528
|
+
key={action.id}
|
|
529
|
+
data-test="ask-ai-action"
|
|
530
|
+
data-status={action.status}
|
|
531
|
+
className="rounded-xl border border-ph-border bg-ph-surface px-3 py-2 text-sm text-ph-ink"
|
|
532
|
+
>
|
|
533
|
+
<p className="font-medium">{action.summary}</p>
|
|
534
|
+
{action.status === "proposed" ? (
|
|
535
|
+
<div className="mt-2 flex flex-wrap gap-2">
|
|
536
|
+
<button
|
|
537
|
+
type="button"
|
|
538
|
+
data-test="ask-ai-action-confirm"
|
|
539
|
+
disabled={Boolean(resolvingId) || !resolveAction}
|
|
540
|
+
onClick={() => void handleResolve(action.id, "confirm")}
|
|
541
|
+
className="inline-flex min-h-8 items-center rounded-lg bg-ph-brand px-2.5 text-xs font-medium text-[color:var(--ph-on-accent)] transition-opacity hover:bg-ph-brand-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40"
|
|
542
|
+
>
|
|
543
|
+
{resolvingId === action.id ? "Working…" : "Confirm"}
|
|
544
|
+
</button>
|
|
545
|
+
<button
|
|
546
|
+
type="button"
|
|
547
|
+
data-test="ask-ai-action-cancel"
|
|
548
|
+
disabled={Boolean(resolvingId) || !resolveAction}
|
|
549
|
+
onClick={() => void handleResolve(action.id, "cancel")}
|
|
550
|
+
className="inline-flex min-h-8 items-center rounded-lg border border-ph-border bg-ph-surface px-2.5 text-xs font-medium text-ph-ink transition-colors hover:bg-ph-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-focus disabled:opacity-40"
|
|
551
|
+
>
|
|
552
|
+
Cancel
|
|
553
|
+
</button>
|
|
554
|
+
</div>
|
|
555
|
+
) : null}
|
|
556
|
+
{action.status === "applied" ? (
|
|
557
|
+
<p className="mt-1 text-xs text-ph-subtle">
|
|
558
|
+
Done
|
|
559
|
+
{action.href ? (
|
|
560
|
+
<>
|
|
561
|
+
{" · "}
|
|
562
|
+
<a
|
|
563
|
+
href={action.href}
|
|
564
|
+
className="text-ph-brand underline-offset-2 hover:underline"
|
|
565
|
+
>
|
|
566
|
+
Open
|
|
567
|
+
</a>
|
|
568
|
+
</>
|
|
569
|
+
) : null}
|
|
570
|
+
</p>
|
|
571
|
+
) : null}
|
|
572
|
+
{action.status === "cancelled" ? (
|
|
573
|
+
<p className="mt-1 text-xs text-ph-subtle">Cancelled</p>
|
|
574
|
+
) : null}
|
|
575
|
+
{action.status === "failed" ? (
|
|
576
|
+
<p className="mt-1 text-xs text-ph-danger">
|
|
577
|
+
{action.error || "Could not apply that."}
|
|
578
|
+
</p>
|
|
579
|
+
) : null}
|
|
580
|
+
</div>
|
|
581
|
+
))}
|
|
404
582
|
</div>
|
|
405
583
|
</div>
|
|
406
584
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { cleanup, render } from "@testing-library/react";
|
|
2
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { SuiteMobileDrawer } from "./suite-mobile-drawer";
|
|
5
|
+
|
|
6
|
+
afterEach(cleanup);
|
|
7
|
+
|
|
8
|
+
describe("SuiteMobileDrawer", () => {
|
|
9
|
+
it("puts one full-height scroller under the close row", () => {
|
|
10
|
+
render(
|
|
11
|
+
<SuiteMobileDrawer
|
|
12
|
+
open
|
|
13
|
+
onClose={() => {}}
|
|
14
|
+
showCloseButton
|
|
15
|
+
title="Kraken"
|
|
16
|
+
>
|
|
17
|
+
<p>Documents</p>
|
|
18
|
+
</SuiteMobileDrawer>,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const body = document.querySelector(
|
|
22
|
+
'[data-test="suite-mobile-drawer-body"]',
|
|
23
|
+
);
|
|
24
|
+
expect(body).not.toBeNull();
|
|
25
|
+
expect(body).toHaveClass(
|
|
26
|
+
"min-h-0",
|
|
27
|
+
"flex-1",
|
|
28
|
+
"overflow-y-auto",
|
|
29
|
+
"suite-scroll-lock",
|
|
30
|
+
);
|
|
31
|
+
expect(body?.previousElementSibling).toHaveClass("h-14", "shrink-0");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -133,7 +133,7 @@ export function SuiteMobileDrawer({
|
|
|
133
133
|
tabIndex={-1}
|
|
134
134
|
data-test={panelDataTest}
|
|
135
135
|
className={cn(
|
|
136
|
-
"
|
|
136
|
+
"relative flex h-dvh max-h-dvh w-[86%] max-w-72 flex-col overflow-hidden bg-ph-surface shadow-xl outline-none motion-safe:transition-transform motion-safe:ease-spring-fast",
|
|
137
137
|
side === "right" && "ml-auto",
|
|
138
138
|
shown
|
|
139
139
|
? "translate-x-0"
|
|
@@ -168,7 +168,14 @@ export function SuiteMobileDrawer({
|
|
|
168
168
|
</button>
|
|
169
169
|
</div>
|
|
170
170
|
) : null}
|
|
171
|
-
{
|
|
171
|
+
{/* One column scroller, same as desktop SuiteSidebar body. Nested
|
|
172
|
+
flex-1 overflow-y-auto regions trap the tree in a leftover strip. */}
|
|
173
|
+
<div
|
|
174
|
+
data-test="suite-mobile-drawer-body"
|
|
175
|
+
className="suite-scroll-lock flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain pb-[max(0.75rem,env(safe-area-inset-bottom))]"
|
|
176
|
+
>
|
|
177
|
+
{children}
|
|
178
|
+
</div>
|
|
172
179
|
</aside>
|
|
173
180
|
</div>
|
|
174
181
|
);
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import Link from "next/link";
|
|
4
|
-
import { useEffect, type ReactNode } from "react";
|
|
4
|
+
import { useCallback, useEffect, type ReactNode } from "react";
|
|
5
5
|
import { NavArrowLeft, Sparks } from "iconoir-react";
|
|
6
6
|
|
|
7
7
|
import { Tooltip } from "../../components/ui/Tooltip";
|
|
8
8
|
import { cn } from "../lib/cn";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
persistSuiteAskAiOpen,
|
|
11
|
+
SUITE_OPEN_ASK_AI_EVENT,
|
|
12
|
+
} from "./ai-panel";
|
|
10
13
|
import type { SuiteNavIcon } from "./suite-bottom-nav";
|
|
11
14
|
|
|
12
15
|
type CollapsedNode = ReactNode | ((collapsed: boolean) => ReactNode);
|
|
@@ -84,32 +87,40 @@ export function SuiteSidebar({
|
|
|
84
87
|
const showAskAi = Boolean(onAskAiOpenChange && askAi);
|
|
85
88
|
const chatOpen = showAskAi && askAiOpen && !collapsed;
|
|
86
89
|
|
|
90
|
+
const setAskAi = useCallback(
|
|
91
|
+
(next: boolean) => {
|
|
92
|
+
persistSuiteAskAiOpen(next);
|
|
93
|
+
onAskAiOpenChange?.(next);
|
|
94
|
+
},
|
|
95
|
+
[onAskAiOpenChange],
|
|
96
|
+
);
|
|
97
|
+
|
|
87
98
|
useEffect(() => {
|
|
88
99
|
if (!onAskAiOpenChange) return;
|
|
89
100
|
function onOpen() {
|
|
90
101
|
onRequestExpand?.();
|
|
91
|
-
|
|
102
|
+
setAskAi(true);
|
|
92
103
|
}
|
|
93
104
|
window.addEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
|
|
94
105
|
return () => window.removeEventListener(SUITE_OPEN_ASK_AI_EVENT, onOpen);
|
|
95
|
-
}, [onAskAiOpenChange, onRequestExpand]);
|
|
106
|
+
}, [onAskAiOpenChange, onRequestExpand, setAskAi]);
|
|
96
107
|
|
|
97
108
|
useEffect(() => {
|
|
98
109
|
if (!chatOpen) return;
|
|
99
110
|
function onKey(event: KeyboardEvent) {
|
|
100
|
-
if (event.key === "Escape")
|
|
111
|
+
if (event.key === "Escape") setAskAi(false);
|
|
101
112
|
}
|
|
102
113
|
window.addEventListener("keydown", onKey);
|
|
103
114
|
return () => window.removeEventListener("keydown", onKey);
|
|
104
|
-
}, [chatOpen,
|
|
115
|
+
}, [chatOpen, setAskAi]);
|
|
105
116
|
|
|
106
117
|
function toggleAskAi() {
|
|
107
118
|
if (askAiOpen) {
|
|
108
|
-
|
|
119
|
+
setAskAi(false);
|
|
109
120
|
return;
|
|
110
121
|
}
|
|
111
122
|
if (collapsed) onRequestExpand?.();
|
|
112
|
-
|
|
123
|
+
setAskAi(true);
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
const askAiButton = (
|
|
@@ -151,7 +162,7 @@ export function SuiteSidebar({
|
|
|
151
162
|
type="button"
|
|
152
163
|
data-test="suite-ask-ai-back"
|
|
153
164
|
aria-label="Back to navigation"
|
|
154
|
-
onClick={() =>
|
|
165
|
+
onClick={() => setAskAi(false)}
|
|
155
166
|
className="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg text-ph-subtle transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand"
|
|
156
167
|
>
|
|
157
168
|
<NavArrowLeft className="h-4 w-4" aria-hidden />
|
|
@@ -213,7 +224,7 @@ export function SuiteSidebar({
|
|
|
213
224
|
<button
|
|
214
225
|
type="button"
|
|
215
226
|
data-test="suite-ask-ai-back"
|
|
216
|
-
onClick={() =>
|
|
227
|
+
onClick={() => setAskAi(false)}
|
|
217
228
|
className="inline-flex h-9 items-center gap-1 rounded-lg px-2 text-sm font-medium text-ph-subtle transition-colors hover:bg-ph-muted hover:text-ph-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ph-brand"
|
|
218
229
|
>
|
|
219
230
|
<NavArrowLeft className="h-4 w-4" aria-hidden />
|
|
@@ -292,10 +303,7 @@ export function SuiteSidebar({
|
|
|
292
303
|
</nav>
|
|
293
304
|
|
|
294
305
|
{secondaryNav ? (
|
|
295
|
-
<div
|
|
296
|
-
className="min-h-0 flex-1"
|
|
297
|
-
data-test="suite-sidebar-secondary-nav"
|
|
298
|
-
>
|
|
306
|
+
<div className="shrink-0" data-test="suite-sidebar-secondary-nav">
|
|
299
307
|
{secondaryNav}
|
|
300
308
|
</div>
|
|
301
309
|
) : null}
|
package/src/suite/index.ts
CHANGED
|
@@ -53,7 +53,17 @@ export {
|
|
|
53
53
|
SuiteAiPanel,
|
|
54
54
|
openSuiteAskAi,
|
|
55
55
|
consumePendingAskAiPrompt,
|
|
56
|
+
persistSuiteAskAiOpen,
|
|
57
|
+
readSuiteAskAiOpen,
|
|
58
|
+
pathWithSuiteAskAiOpen,
|
|
59
|
+
consumeSuiteAskAiOpenFromLocation,
|
|
56
60
|
SUITE_OPEN_ASK_AI_EVENT,
|
|
61
|
+
SUITE_ASK_AI_OPEN_KEY,
|
|
62
|
+
SUITE_ASK_AI_OPEN_QUERY,
|
|
63
|
+
SUITE_MUTATED_EVENT,
|
|
64
|
+
TURTLETIME_TIMER_MUTATED_EVENT,
|
|
65
|
+
type SuiteAiAction,
|
|
66
|
+
type SuiteAiActionStatus,
|
|
57
67
|
type SuiteAiPreset,
|
|
58
68
|
type SuiteAiChatMessage,
|
|
59
69
|
type SuiteAskAiOpenDetail,
|