@tangle-network/sandbox-ui 0.41.0 → 0.43.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/dist/assistant.d.ts +36 -2
- package/dist/assistant.js +121 -26
- package/dist/{chunk-6UDHPI2R.js → chunk-KCWIC3SJ.js} +24 -10
- package/dist/dashboard.d.ts +7 -1
- package/dist/dashboard.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/assistant.d.ts
CHANGED
|
@@ -38,6 +38,9 @@ interface ReasoningEventData {
|
|
|
38
38
|
interface ToolCallEventData {
|
|
39
39
|
callId: string;
|
|
40
40
|
name: string;
|
|
41
|
+
/** The parsed arguments the agent invoked the tool with. Lets a renderer show
|
|
42
|
+
* exactly what was called. Omitted by servers predating the field. */
|
|
43
|
+
args?: Record<string, unknown>;
|
|
41
44
|
}
|
|
42
45
|
interface ToolResultEventData {
|
|
43
46
|
callId: string;
|
|
@@ -84,6 +87,9 @@ interface UsageEventData {
|
|
|
84
87
|
completionTokens: number | null;
|
|
85
88
|
costUsd: number | null;
|
|
86
89
|
balanceUsd: number | null;
|
|
90
|
+
/** Wall-clock duration of the turn in milliseconds, when the server measures
|
|
91
|
+
* it. Drives the renderer's tokens/sec figure. Omitted by older servers. */
|
|
92
|
+
durationMs?: number | null;
|
|
87
93
|
/** True when a completed turn was replayed from storage (no charge). */
|
|
88
94
|
replayed?: boolean;
|
|
89
95
|
}
|
|
@@ -151,11 +157,12 @@ interface ChatMessage {
|
|
|
151
157
|
id: string;
|
|
152
158
|
role: ChatRole;
|
|
153
159
|
text: string;
|
|
154
|
-
/** Present only on `tool` messages — the activity's tool name,
|
|
155
|
-
* finished) its outcome. */
|
|
160
|
+
/** Present only on `tool` messages — the activity's tool name, the arguments it
|
|
161
|
+
* was called with, its state, and (once finished) its outcome. */
|
|
156
162
|
tool?: {
|
|
157
163
|
name: string;
|
|
158
164
|
status: ToolActivityStatus;
|
|
165
|
+
args?: Record<string, unknown>;
|
|
159
166
|
outcome?: ToolOutcome;
|
|
160
167
|
};
|
|
161
168
|
}
|
|
@@ -176,6 +183,13 @@ interface PendingProposal {
|
|
|
176
183
|
interface UsageInfo {
|
|
177
184
|
costUsd: number | null;
|
|
178
185
|
balanceUsd: number | null;
|
|
186
|
+
/** Token counts + wall-clock duration for the settled turn, when the server
|
|
187
|
+
* reports them — drive per-message tokens/sec + cost in a renderer. Optional
|
|
188
|
+
* so a consumer constructing this on the prior `{ costUsd, balanceUsd,
|
|
189
|
+
* replayed }` shape stays valid; a renderer treats a missing value as null. */
|
|
190
|
+
promptTokens?: number | null;
|
|
191
|
+
completionTokens?: number | null;
|
|
192
|
+
durationMs?: number | null;
|
|
179
193
|
replayed: boolean;
|
|
180
194
|
}
|
|
181
195
|
/**
|
|
@@ -202,6 +216,10 @@ interface AssistantTranscriptView {
|
|
|
202
216
|
* (drives a "thinking" affordance). */
|
|
203
217
|
isThinking: boolean;
|
|
204
218
|
pendingProposals: PendingProposal[];
|
|
219
|
+
/** Cost/tokens/duration for the most recently settled turn, or null before any
|
|
220
|
+
* turn settles. Optional so a host predating the field stays a valid consumer;
|
|
221
|
+
* a renderer treats a missing value the same as null. */
|
|
222
|
+
usage?: UsageInfo | null;
|
|
205
223
|
/** The panel's bound proposal card for a pending proposal — render it where the
|
|
206
224
|
* confirm/cancel UI should appear. */
|
|
207
225
|
renderProposal: (proposal: PendingProposal) => ReactNode;
|
|
@@ -303,6 +321,13 @@ interface AssistantClient {
|
|
|
303
321
|
fetchThreadHistory(threadId: string, signal?: AbortSignal): Promise<ThreadHistoryResult>;
|
|
304
322
|
streamChat(req: ChatRequest, onEvent: (event: AssistantStreamEvent) => void, signal: AbortSignal): Promise<void>;
|
|
305
323
|
confirmProposal(proposalId: string): Promise<ConfirmResult>;
|
|
324
|
+
/** Delete a thread and its server-side turns/proposals. Resolves `{ ok }`; a
|
|
325
|
+
* 404 (already gone) is treated as success so a double-delete is harmless.
|
|
326
|
+
* Optional so a host with no delete endpoint stays a valid client — the panel
|
|
327
|
+
* hides the delete affordance when it's absent (see `useAssistantThreads`). */
|
|
328
|
+
deleteThread?(threadId: string): Promise<{
|
|
329
|
+
ok: boolean;
|
|
330
|
+
}>;
|
|
306
331
|
}
|
|
307
332
|
/**
|
|
308
333
|
* Build an assistant client bound to one host's transport. The returned methods
|
|
@@ -450,6 +475,15 @@ interface AssistantThreads {
|
|
|
450
475
|
/** Load (or reload) the thread list. Must be called to populate `threads` —
|
|
451
476
|
* the hook never fetches on mount (the panel calls this when history opens). */
|
|
452
477
|
refresh: () => void;
|
|
478
|
+
/** Delete a thread. Optimistically drops it from the list (within the current
|
|
479
|
+
* owner scope); on failure the list is reloaded to restore the true state. A
|
|
480
|
+
* no-op resolving `{ ok: false }` when the client has no `deleteThread`. */
|
|
481
|
+
remove: (threadId: string) => Promise<{
|
|
482
|
+
ok: boolean;
|
|
483
|
+
}>;
|
|
484
|
+
/** Whether the configured client supports deletion — drives whether a host
|
|
485
|
+
* shows the delete affordance. */
|
|
486
|
+
canRemove: boolean;
|
|
453
487
|
}
|
|
454
488
|
declare function useAssistantThreads(userId: string | null): AssistantThreads;
|
|
455
489
|
|
package/dist/assistant.js
CHANGED
|
@@ -443,6 +443,18 @@ function createAssistantClient(config) {
|
|
|
443
443
|
ok: false,
|
|
444
444
|
error: body?.error?.message ?? "The action could not be completed"
|
|
445
445
|
};
|
|
446
|
+
},
|
|
447
|
+
async deleteThread(threadId) {
|
|
448
|
+
try {
|
|
449
|
+
const res = await fetch(url(`/threads/${encodeURIComponent(threadId)}`), {
|
|
450
|
+
method: "DELETE",
|
|
451
|
+
headers: authHeaders(),
|
|
452
|
+
credentials
|
|
453
|
+
});
|
|
454
|
+
return { ok: res.ok || res.status === 404 };
|
|
455
|
+
} catch {
|
|
456
|
+
return { ok: false };
|
|
457
|
+
}
|
|
446
458
|
}
|
|
447
459
|
};
|
|
448
460
|
}
|
|
@@ -845,7 +857,11 @@ function applyStreamEvent(state, event) {
|
|
|
845
857
|
id: chipId,
|
|
846
858
|
role: "tool",
|
|
847
859
|
text: "",
|
|
848
|
-
tool: {
|
|
860
|
+
tool: {
|
|
861
|
+
name: event.data.name,
|
|
862
|
+
status: "running",
|
|
863
|
+
args: event.data.args
|
|
864
|
+
}
|
|
849
865
|
};
|
|
850
866
|
return {
|
|
851
867
|
...state,
|
|
@@ -865,7 +881,14 @@ function applyStreamEvent(state, event) {
|
|
|
865
881
|
(m) => m.id === chipId ? {
|
|
866
882
|
...m,
|
|
867
883
|
text: errText,
|
|
868
|
-
|
|
884
|
+
// Preserve the args the matching tool_call recorded — the
|
|
885
|
+
// result event doesn't carry them.
|
|
886
|
+
tool: {
|
|
887
|
+
name: event.data.name,
|
|
888
|
+
status,
|
|
889
|
+
args: m.tool?.args,
|
|
890
|
+
outcome
|
|
891
|
+
}
|
|
869
892
|
} : m
|
|
870
893
|
)
|
|
871
894
|
};
|
|
@@ -893,6 +916,9 @@ function applyStreamEvent(state, event) {
|
|
|
893
916
|
usage: {
|
|
894
917
|
costUsd: event.data.costUsd,
|
|
895
918
|
balanceUsd: event.data.balanceUsd,
|
|
919
|
+
promptTokens: event.data.promptTokens,
|
|
920
|
+
completionTokens: event.data.completionTokens,
|
|
921
|
+
durationMs: event.data.durationMs ?? null,
|
|
896
922
|
replayed: event.data.replayed ?? false
|
|
897
923
|
}
|
|
898
924
|
};
|
|
@@ -1406,6 +1432,7 @@ function useAssistantThreads(userId) {
|
|
|
1406
1432
|
const clientRef = useRef2(client);
|
|
1407
1433
|
clientRef.current = client;
|
|
1408
1434
|
const abortRef = useRef2(null);
|
|
1435
|
+
const pendingDeletesRef = useRef2(/* @__PURE__ */ new Set());
|
|
1409
1436
|
const [state, setState] = useState2(() => ({
|
|
1410
1437
|
threads: [],
|
|
1411
1438
|
loading: false,
|
|
@@ -1440,7 +1467,11 @@ function useAssistantThreads(userId) {
|
|
|
1440
1467
|
if (!isCurrent()) return;
|
|
1441
1468
|
setState((s) => ({
|
|
1442
1469
|
// null = transient failure: keep the prior list, just drop the spinner.
|
|
1443
|
-
|
|
1470
|
+
// Drop any in-flight/finished deletions so a stale fetch can't resurrect
|
|
1471
|
+
// a row we already removed.
|
|
1472
|
+
threads: (result ?? s.threads).filter(
|
|
1473
|
+
(t) => !pendingDeletesRef.current.has(t.id)
|
|
1474
|
+
),
|
|
1444
1475
|
loading: false,
|
|
1445
1476
|
loaded: true,
|
|
1446
1477
|
ownerUserId: requestedUserId,
|
|
@@ -1452,13 +1483,45 @@ function useAssistantThreads(userId) {
|
|
|
1452
1483
|
}
|
|
1453
1484
|
});
|
|
1454
1485
|
}, []);
|
|
1455
|
-
|
|
1486
|
+
const remove = useCallback2(
|
|
1487
|
+
async (threadId) => {
|
|
1488
|
+
const requestedClient = clientRef.current;
|
|
1489
|
+
const requestedUserId = userRef.current;
|
|
1490
|
+
if (!requestedClient.deleteThread) return { ok: false };
|
|
1491
|
+
pendingDeletesRef.current.add(threadId);
|
|
1492
|
+
setState(
|
|
1493
|
+
(s) => s.ownerClient === requestedClient && s.ownerUserId === requestedUserId ? { ...s, threads: s.threads.filter((t) => t.id !== threadId) } : s
|
|
1494
|
+
);
|
|
1495
|
+
let res;
|
|
1496
|
+
try {
|
|
1497
|
+
res = await requestedClient.deleteThread(threadId);
|
|
1498
|
+
} catch {
|
|
1499
|
+
res = { ok: false };
|
|
1500
|
+
}
|
|
1501
|
+
if (!res.ok) {
|
|
1502
|
+
pendingDeletesRef.current.delete(threadId);
|
|
1503
|
+
if (userRef.current === requestedUserId && clientRef.current === requestedClient) {
|
|
1504
|
+
refresh();
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return res;
|
|
1508
|
+
},
|
|
1509
|
+
[refresh]
|
|
1510
|
+
);
|
|
1511
|
+
useEffect3(() => {
|
|
1512
|
+
return () => abortRef.current?.abort();
|
|
1513
|
+
}, [userId, client]);
|
|
1514
|
+
useEffect3(() => {
|
|
1515
|
+
return () => pendingDeletesRef.current.clear();
|
|
1516
|
+
}, []);
|
|
1456
1517
|
const stale = state.ownerUserId !== userId || state.ownerClient !== client;
|
|
1457
1518
|
return {
|
|
1458
1519
|
threads: stale ? [] : state.threads,
|
|
1459
1520
|
loading: stale ? false : state.loading,
|
|
1460
1521
|
loaded: stale ? false : state.loaded,
|
|
1461
|
-
refresh
|
|
1522
|
+
refresh,
|
|
1523
|
+
remove,
|
|
1524
|
+
canRemove: typeof client.deleteThread === "function"
|
|
1462
1525
|
};
|
|
1463
1526
|
}
|
|
1464
1527
|
|
|
@@ -1466,13 +1529,13 @@ function useAssistantThreads(userId) {
|
|
|
1466
1529
|
import { MessageSquare } from "lucide-react";
|
|
1467
1530
|
import {
|
|
1468
1531
|
useEffect as useEffect5,
|
|
1469
|
-
useRef as
|
|
1532
|
+
useRef as useRef6
|
|
1470
1533
|
} from "react";
|
|
1471
1534
|
|
|
1472
1535
|
// src/assistant/AssistantPanel.tsx
|
|
1473
1536
|
import { AgentTimeline, ChatInput } from "@tangle-network/ui/chat";
|
|
1474
|
-
import { History, Minus, Plus, RotateCcw, X } from "lucide-react";
|
|
1475
|
-
import { useState as useState5 } from "react";
|
|
1537
|
+
import { History, Minus, Plus, RotateCcw, Trash2, X } from "lucide-react";
|
|
1538
|
+
import { useRef as useRef4, useState as useState5 } from "react";
|
|
1476
1539
|
|
|
1477
1540
|
// src/assistant/build-timeline.tsx
|
|
1478
1541
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
@@ -1841,6 +1904,8 @@ function AssistantPanel({
|
|
|
1841
1904
|
const font = useFontScale();
|
|
1842
1905
|
const [historyOpen, setHistoryOpen] = useState5(false);
|
|
1843
1906
|
const { state } = chat;
|
|
1907
|
+
const chatRef = useRef4(chat);
|
|
1908
|
+
chatRef.current = chat;
|
|
1844
1909
|
const effectiveBalance = state.usage?.balanceUsd ?? balanceUsd;
|
|
1845
1910
|
const errorView = state.error ? presentError(state.error.code, state.error.message) : null;
|
|
1846
1911
|
const low = isLowBalance(effectiveBalance) && !errorView;
|
|
@@ -1864,6 +1929,18 @@ function AssistantPanel({
|
|
|
1864
1929
|
return next;
|
|
1865
1930
|
});
|
|
1866
1931
|
};
|
|
1932
|
+
const deleteThread = async (threadId) => {
|
|
1933
|
+
const pre = chatRef.current.state;
|
|
1934
|
+
if (pre.threadId === threadId && pre.status !== "idle") return;
|
|
1935
|
+
if (!window.confirm("Delete this conversation? This can't be undone.")) {
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1938
|
+
const res = await threads.remove(threadId);
|
|
1939
|
+
const live = chatRef.current.state;
|
|
1940
|
+
if (res.ok && live.threadId === threadId && live.status === "idle") {
|
|
1941
|
+
chatRef.current.reset();
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1867
1944
|
return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col bg-background", children: [
|
|
1868
1945
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between border-border border-b px-4 py-3", children: [
|
|
1869
1946
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-baseline gap-2", children: [
|
|
@@ -1958,18 +2035,35 @@ function AssistantPanel({
|
|
|
1958
2035
|
}
|
|
1959
2036
|
)
|
|
1960
2037
|
] }),
|
|
1961
|
-
historyOpen && /* @__PURE__ */ jsx4("div", { className: "max-h-48 overflow-y-auto border-border border-b", children: threads.threads.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-4 py-3 text-muted-foreground text-xs", children: threads.loaded ? "No past conversations." : "Loading\u2026" }) : /* @__PURE__ */ jsx4("ul", { children: threads.threads.map((t) =>
|
|
1962
|
-
"
|
|
1963
|
-
{
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2038
|
+
historyOpen && /* @__PURE__ */ jsx4("div", { className: "max-h-48 overflow-y-auto border-border border-b", children: threads.threads.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-4 py-3 text-muted-foreground text-xs", children: threads.loaded ? "No past conversations." : "Loading\u2026" }) : /* @__PURE__ */ jsx4("ul", { children: threads.threads.map((t) => {
|
|
2039
|
+
const busyActive = state.threadId === t.id && state.status !== "idle";
|
|
2040
|
+
return /* @__PURE__ */ jsxs2("li", { className: "group flex items-center hover:bg-muted/50", children: [
|
|
2041
|
+
/* @__PURE__ */ jsx4(
|
|
2042
|
+
"button",
|
|
2043
|
+
{
|
|
2044
|
+
type: "button",
|
|
2045
|
+
onClick: () => {
|
|
2046
|
+
chat.switchThread(t.id);
|
|
2047
|
+
setHistoryOpen(false);
|
|
2048
|
+
},
|
|
2049
|
+
className: "min-w-0 flex-1 truncate px-4 py-2 text-left text-foreground text-xs",
|
|
2050
|
+
children: t.title ?? "Untitled conversation"
|
|
2051
|
+
}
|
|
2052
|
+
),
|
|
2053
|
+
threads.canRemove && /* @__PURE__ */ jsx4(
|
|
2054
|
+
"button",
|
|
2055
|
+
{
|
|
2056
|
+
type: "button",
|
|
2057
|
+
onClick: () => void deleteThread(t.id),
|
|
2058
|
+
disabled: busyActive,
|
|
2059
|
+
"aria-label": "Delete conversation",
|
|
2060
|
+
title: busyActive ? "Can't delete while this conversation is active" : "Delete conversation",
|
|
2061
|
+
className: "shrink-0 p-2 text-muted-foreground opacity-0 transition hover:text-destructive focus-visible:opacity-100 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-30",
|
|
2062
|
+
children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3.5 w-3.5" })
|
|
2063
|
+
}
|
|
2064
|
+
)
|
|
2065
|
+
] }, t.id);
|
|
2066
|
+
}) }) }),
|
|
1973
2067
|
/* @__PURE__ */ jsx4(
|
|
1974
2068
|
"div",
|
|
1975
2069
|
{
|
|
@@ -1986,6 +2080,7 @@ function AssistantPanel({
|
|
|
1986
2080
|
isStreaming: streaming,
|
|
1987
2081
|
isThinking,
|
|
1988
2082
|
pendingProposals: state.pendingProposals,
|
|
2083
|
+
usage: state.usage,
|
|
1989
2084
|
renderProposal
|
|
1990
2085
|
}) : /* @__PURE__ */ jsx4(
|
|
1991
2086
|
AgentTimeline,
|
|
@@ -2092,7 +2187,7 @@ function useAssistantLauncher() {
|
|
|
2092
2187
|
}
|
|
2093
2188
|
|
|
2094
2189
|
// src/assistant/ResizeHandle.tsx
|
|
2095
|
-
import { useRef as
|
|
2190
|
+
import { useRef as useRef5 } from "react";
|
|
2096
2191
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
2097
2192
|
function ResizeHandle({
|
|
2098
2193
|
width,
|
|
@@ -2101,7 +2196,7 @@ function ResizeHandle({
|
|
|
2101
2196
|
onCommit,
|
|
2102
2197
|
onNudge
|
|
2103
2198
|
}) {
|
|
2104
|
-
const dragRef =
|
|
2199
|
+
const dragRef = useRef5(null);
|
|
2105
2200
|
const onPointerDown = (e) => {
|
|
2106
2201
|
if (e.button !== 0) return;
|
|
2107
2202
|
e.preventDefault();
|
|
@@ -2190,10 +2285,10 @@ function AssistantDock({
|
|
|
2190
2285
|
const chat = useAssistantChat(userId, { onWorkflowMutation });
|
|
2191
2286
|
const isDesktop = useIsDesktop();
|
|
2192
2287
|
const { width, maxWidth, setWidth, previewWidth, nudgeWidth } = usePanelWidth();
|
|
2193
|
-
const launcherRef =
|
|
2194
|
-
const dialogRef =
|
|
2195
|
-
const returnFocusRef =
|
|
2196
|
-
const wasOpenRef =
|
|
2288
|
+
const launcherRef = useRef6(null);
|
|
2289
|
+
const dialogRef = useRef6(null);
|
|
2290
|
+
const returnFocusRef = useRef6(null);
|
|
2291
|
+
const wasOpenRef = useRef6(false);
|
|
2197
2292
|
useEffect5(() => {
|
|
2198
2293
|
if (!open) return;
|
|
2199
2294
|
const onKeyDown = (e) => {
|
|
@@ -1543,9 +1543,9 @@ function DashboardLayoutInner({
|
|
|
1543
1543
|
);
|
|
1544
1544
|
const activePanel = panels.find((p) => p.mode === mode);
|
|
1545
1545
|
const buildSidebarContent = React6.useCallback(
|
|
1546
|
-
(showLabels) => /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1546
|
+
(showLabels, allowCollapse) => /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1547
1547
|
/* @__PURE__ */ jsxs13(SidebarRail, { wide: showLabels, children: [
|
|
1548
|
-
/* @__PURE__ */ jsx14(SidebarRailHeader, { children: onLogoClick ? /* @__PURE__ */ jsx14(
|
|
1548
|
+
/* @__PURE__ */ jsx14(SidebarRailHeader, { className: cn(showLabels && "justify-start px-3"), children: onLogoClick ? /* @__PURE__ */ jsx14(
|
|
1549
1549
|
"button",
|
|
1550
1550
|
{
|
|
1551
1551
|
type: "button",
|
|
@@ -1573,12 +1573,17 @@ function DashboardLayoutInner({
|
|
|
1573
1573
|
children: /* @__PURE__ */ jsx14(Logo, { variant, size: "sm", iconOnly: true })
|
|
1574
1574
|
}
|
|
1575
1575
|
) }),
|
|
1576
|
-
/* @__PURE__ */ jsx14(SidebarRailNav, { children: navItems.map((item, i) => {
|
|
1576
|
+
/* @__PURE__ */ jsx14(SidebarRailNav, { className: showLabels ? "px-2" : void 0, children: navItems.map((item, i) => {
|
|
1577
1577
|
const isMode = modeSet.has(item.id);
|
|
1578
1578
|
const prevIsMode = i > 0 && modeSet.has(navItems[i - 1].id);
|
|
1579
1579
|
const showSep = i > 0 && isMode && !prevIsMode;
|
|
1580
1580
|
return /* @__PURE__ */ jsxs13(React6.Fragment, { children: [
|
|
1581
|
-
showSep && /* @__PURE__ */ jsx14(
|
|
1581
|
+
showSep && /* @__PURE__ */ jsx14(
|
|
1582
|
+
RailSeparator,
|
|
1583
|
+
{
|
|
1584
|
+
className: showLabels ? "w-full" : void 0
|
|
1585
|
+
}
|
|
1586
|
+
),
|
|
1582
1587
|
isMode ? /* @__PURE__ */ jsx14(
|
|
1583
1588
|
RailModeButton,
|
|
1584
1589
|
{
|
|
@@ -1601,10 +1606,18 @@ function DashboardLayoutInner({
|
|
|
1601
1606
|
)
|
|
1602
1607
|
] }, item.id);
|
|
1603
1608
|
}) }),
|
|
1604
|
-
/* @__PURE__ */ jsxs13(SidebarRailFooter, { children: [
|
|
1609
|
+
/* @__PURE__ */ jsxs13(SidebarRailFooter, { className: cn(showLabels && "items-stretch px-2"), children: [
|
|
1610
|
+
allowCollapse && /* @__PURE__ */ jsx14(
|
|
1611
|
+
RailCollapseToggle,
|
|
1612
|
+
{
|
|
1613
|
+
collapsed: railCollapsed,
|
|
1614
|
+
showLabel: showLabels,
|
|
1615
|
+
onToggle: toggleRail
|
|
1616
|
+
}
|
|
1617
|
+
),
|
|
1605
1618
|
onSettingsClick ? /* @__PURE__ */ jsx14(RailButton, { icon: SettingsIconSmall, label: "Settings", onClick: onSettingsClick, showLabel: showLabels }) : /* @__PURE__ */ jsx14(RailButton, { icon: SettingsIconSmall, label: "Settings", showLabel: showLabels, asChild: true, children: /* @__PURE__ */ jsx14(Link, { href: settingsHref, to: settingsHref }) }),
|
|
1606
1619
|
railFooter,
|
|
1607
|
-
/* @__PURE__ */ jsx14(RailSeparator, {}),
|
|
1620
|
+
/* @__PURE__ */ jsx14(RailSeparator, { className: showLabels ? "w-full" : void 0 }),
|
|
1608
1621
|
/* @__PURE__ */ jsx14(
|
|
1609
1622
|
ProfileAvatar,
|
|
1610
1623
|
{
|
|
@@ -1613,6 +1626,7 @@ function DashboardLayoutInner({
|
|
|
1613
1626
|
onLogout,
|
|
1614
1627
|
onSettingsClick,
|
|
1615
1628
|
settingsHref,
|
|
1629
|
+
showDetails: showLabels,
|
|
1616
1630
|
LinkComponent,
|
|
1617
1631
|
children: profileMenuItems
|
|
1618
1632
|
}
|
|
@@ -1651,10 +1665,10 @@ function DashboardLayoutInner({
|
|
|
1651
1665
|
]
|
|
1652
1666
|
);
|
|
1653
1667
|
const sidebarContent = React6.useMemo(
|
|
1654
|
-
() => buildSidebarContent(labeledRail && !railCollapsed),
|
|
1668
|
+
() => buildSidebarContent(labeledRail && !railCollapsed, labeledRail),
|
|
1655
1669
|
[buildSidebarContent, labeledRail, railCollapsed]
|
|
1656
1670
|
);
|
|
1657
|
-
const mobileSidebarContent = React6.useMemo(() => buildSidebarContent(true), [buildSidebarContent]);
|
|
1671
|
+
const mobileSidebarContent = React6.useMemo(() => buildSidebarContent(true, false), [buildSidebarContent]);
|
|
1658
1672
|
return /* @__PURE__ */ jsxs13("div", { className: cn("min-h-screen bg-background text-foreground", className), children: [
|
|
1659
1673
|
/* @__PURE__ */ jsxs13(
|
|
1660
1674
|
"nav",
|
|
@@ -1783,8 +1797,8 @@ function DashboardLayoutInner({
|
|
|
1783
1797
|
footer
|
|
1784
1798
|
] });
|
|
1785
1799
|
}
|
|
1786
|
-
function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, ...props }) {
|
|
1787
|
-
return /* @__PURE__ */ jsx14(SidebarProvider, { defaultPanelOpen, defaultMode, labeledRail, hasPanels: (props.panels?.length ?? 0) > 0, children: /* @__PURE__ */ jsx14(DashboardLayoutInner, { defaultPanelOpen, defaultMode, labeledRail, ...props }) });
|
|
1800
|
+
function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, ...props }) {
|
|
1801
|
+
return /* @__PURE__ */ jsx14(SidebarProvider, { defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, hasPanels: (props.panels?.length ?? 0) > 0, children: /* @__PURE__ */ jsx14(DashboardLayoutInner, { defaultPanelOpen, defaultMode, labeledRail, ...props }) });
|
|
1788
1802
|
}
|
|
1789
1803
|
|
|
1790
1804
|
// src/dashboard/sidebar-layout.tsx
|
package/dist/dashboard.d.ts
CHANGED
|
@@ -681,6 +681,12 @@ interface DashboardLayoutProps {
|
|
|
681
681
|
* toggles between icon-only and labeled instead of navigating.
|
|
682
682
|
*/
|
|
683
683
|
labeledRail?: boolean;
|
|
684
|
+
/**
|
|
685
|
+
* Initial rail-collapsed state when `labeledRail` is set and the rail is
|
|
686
|
+
* uncontrolled. Defaults to expanded (`false`). Pass `true` to start on the
|
|
687
|
+
* compact icon rail — the user's choice then persists to localStorage.
|
|
688
|
+
*/
|
|
689
|
+
defaultRailCollapsed?: boolean;
|
|
684
690
|
footer?: React.ReactNode;
|
|
685
691
|
defaultPanelOpen?: boolean;
|
|
686
692
|
defaultMode?: string;
|
|
@@ -702,7 +708,7 @@ interface DashboardLayoutProps {
|
|
|
702
708
|
onMarkAllRead?: () => void;
|
|
703
709
|
};
|
|
704
710
|
}
|
|
705
|
-
declare function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, ...props }: DashboardLayoutProps): react_jsx_runtime.JSX.Element;
|
|
711
|
+
declare function DashboardLayout({ defaultPanelOpen, defaultMode, labeledRail, defaultRailCollapsed, ...props }: DashboardLayoutProps): react_jsx_runtime.JSX.Element;
|
|
706
712
|
|
|
707
713
|
/** A sub-destination revealed inside a nav item's right-flyout. */
|
|
708
714
|
interface SidebarLayoutFlyoutItem {
|
package/dist/dashboard.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED