@tangle-network/ui 11.12.0 → 11.13.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 +17 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/auth/auth.d.ts +53 -0
- package/dist/{auth-DOETfnd9.js → auth/auth.js} +7 -37
- package/dist/auth/login-layout.d.ts +20 -0
- package/dist/auth/login-layout.js +36 -0
- package/dist/auth.d.ts +2 -1
- package/dist/auth.js +2 -1
- package/dist/chat/agent-timeline.d.ts +83 -0
- package/dist/chat/agent-timeline.js +236 -0
- package/dist/chat/chat-container.d.ts +51 -0
- package/dist/chat/chat-container.js +309 -0
- package/dist/chat/chat-message.d.ts +22 -0
- package/dist/chat/chat-message.js +38 -0
- package/dist/chat/message-list.d.ts +32 -0
- package/dist/chat/message-list.js +37 -0
- package/dist/chat/thinking-indicator.d.ts +6 -0
- package/dist/chat/thinking-indicator.js +38 -0
- package/dist/chat/user-message.d.ts +21 -0
- package/dist/chat/user-message.js +42 -0
- package/dist/chat.d.ts +6 -1
- package/dist/chat.js +6 -1
- package/dist/editor/document-editor-pane.d.ts +37 -0
- package/dist/editor/document-editor-pane.js +199 -0
- package/dist/editor/editor-lazy.js +37 -0
- package/dist/editor/editor-loading.js +21 -0
- package/dist/editor/editor-peers.d.ts +6 -0
- package/dist/editor/editor-peers.js +155 -0
- package/dist/editor/editor-provider.d.ts +97 -0
- package/dist/editor/editor-provider.js +253 -0
- package/dist/editor/editor-toolbar.d.ts +11 -0
- package/dist/editor/editor-toolbar.js +106 -0
- package/dist/editor/markdown-conversion.js +23 -0
- package/dist/editor/markdown-document-editor.js +103 -0
- package/dist/editor/tiptap-editor.d.ts +45 -0
- package/dist/editor/tiptap-editor.js +271 -0
- package/dist/editor/use-editor.d.ts +67 -0
- package/dist/editor/use-editor.js +158 -0
- package/dist/editor.d.ts +6 -119
- package/dist/editor.js +5 -1
- package/dist/files/file-artifact-pane.d.ts +47 -0
- package/dist/files/file-artifact-pane.js +114 -0
- package/dist/files/file-format.d.ts +51 -0
- package/dist/{file-format-BgXbr-Ib.js → files/file-format.js} +1 -1
- package/dist/files/file-preview.d.ts +27 -0
- package/dist/files/file-preview.js +359 -0
- package/dist/files/file-tabs.d.ts +19 -0
- package/dist/files/file-tabs.js +48 -0
- package/dist/files/file-tree.d.ts +31 -0
- package/dist/files/file-tree.js +135 -0
- package/dist/files/rich-file-tree.d.ts +62 -0
- package/dist/files/rich-file-tree.js +122 -0
- package/dist/files.d.ts +6 -1
- package/dist/files.js +6 -2
- package/dist/hooks/use-auth.d.ts +40 -0
- package/dist/hooks/use-auth.js +91 -0
- package/dist/hooks/use-auto-scroll.d.ts +11 -0
- package/dist/hooks/use-auto-scroll.js +48 -0
- package/dist/hooks/use-dropdown-menu.d.ts +12 -0
- package/dist/hooks/use-dropdown-menu.js +31 -0
- package/dist/hooks/use-live-time.d.ts +7 -0
- package/dist/hooks/use-live-time.js +22 -0
- package/dist/hooks/use-realtime-session.d.ts +32 -0
- package/dist/hooks/use-realtime-session.js +235 -0
- package/dist/hooks/use-run-collapse-state.d.ts +13 -0
- package/dist/hooks/use-run-collapse-state.js +24 -0
- package/dist/hooks/use-run-groups.d.ts +11 -0
- package/dist/hooks/use-run-groups.js +85 -0
- package/dist/hooks/use-sdk-session.d.ts +59 -0
- package/dist/hooks/use-sdk-session.js +322 -0
- package/dist/hooks/use-sse-stream.d.ts +124 -0
- package/dist/hooks/use-sse-stream.js +229 -0
- package/dist/hooks/use-tool-call-stream.d.ts +23 -0
- package/dist/hooks/use-tool-call-stream.js +75 -0
- package/dist/hooks.d.ts +11 -49
- package/dist/hooks.js +10 -110
- package/dist/index.d.ts +99 -25
- package/dist/index.js +86 -36
- package/dist/lib/focus.d.ts +55 -0
- package/dist/{focus-BcE_SHqz.js → lib/focus.js} +1 -1
- package/dist/lib/utils.d.ts +4 -0
- package/dist/{utils-C_uf36nf.js → lib/utils.js} +1 -1
- package/dist/{code-block-B9miUPht.d.ts → markdown/code-block.d.ts} +5 -6
- package/dist/{code-block-B36KVUSN.js → markdown/code-block.js} +22 -47
- package/dist/{markdown-C0atSabr.d.ts → markdown/markdown.d.ts} +4 -5
- package/dist/{markdown-Db08KAYF.js → markdown/markdown.js} +3 -3
- package/dist/markdown.d.ts +2 -2
- package/dist/markdown.js +2 -2
- package/dist/{openui-artifact-renderer-fkBQaoLO.d.ts → openui/openui-artifact-renderer.d.ts} +20 -21
- package/dist/{openui-artifact-renderer-DG_i2lOR.js → openui/openui-artifact-renderer.js} +9 -7
- package/dist/{schema-BpW9rSSG.js → openui/schema-compiled.js} +2 -354
- package/dist/{schema-DAstk-J4.d.ts → openui/schema-data.d.ts} +3 -24
- package/dist/openui/schema-data.js +206 -0
- package/dist/openui-schema.d.ts +22 -3
- package/dist/openui-schema.js +138 -1
- package/dist/openui.d.ts +3 -2
- package/dist/openui.js +3 -2
- package/dist/{artifact-pane-BL_DFWNl.d.ts → primitives/artifact-pane.d.ts} +3 -4
- package/dist/{artifact-pane-DMXT9a8U.js → primitives/artifact-pane.js} +2 -2
- package/dist/primitives/avatar.d.ts +8 -0
- package/dist/primitives/avatar.js +25 -0
- package/dist/primitives/badge.d.ts +22 -0
- package/dist/primitives/badge.js +47 -0
- package/dist/{button-yoIv77e2.d.ts → primitives/button.d.ts} +2 -2
- package/dist/primitives/button.js +113 -0
- package/dist/primitives/card.d.ts +18 -0
- package/dist/primitives/card.js +52 -0
- package/dist/primitives/code-block.d.ts +6 -0
- package/dist/primitives/code-block.js +13 -0
- package/dist/primitives/dialog.d.ts +23 -0
- package/dist/primitives/dialog.js +59 -0
- package/dist/primitives/drop-zone.d.ts +23 -0
- package/dist/primitives/drop-zone.js +104 -0
- package/dist/primitives/dropdown-menu.d.ts +29 -0
- package/dist/{dropdown-menu-BpcLa5vF.js → primitives/dropdown-menu.js} +2 -23
- package/dist/primitives/empty-state.d.ts +11 -0
- package/dist/primitives/empty-state.js +32 -0
- package/dist/primitives/heading.d.ts +23 -0
- package/dist/primitives/heading.js +38 -0
- package/dist/primitives/input.d.ts +23 -0
- package/dist/primitives/input.js +110 -0
- package/dist/primitives/label.d.ts +6 -0
- package/dist/primitives/label.js +16 -0
- package/dist/primitives/metric-strip.d.ts +48 -0
- package/dist/primitives/metric-strip.js +52 -0
- package/dist/primitives/page-header.d.ts +25 -0
- package/dist/primitives/page-header.js +47 -0
- package/dist/primitives/page-shell.d.ts +14 -0
- package/dist/primitives/page-shell.js +17 -0
- package/dist/primitives/progress.d.ts +9 -0
- package/dist/primitives/progress.js +29 -0
- package/dist/primitives/segmented-control.d.ts +40 -0
- package/dist/primitives/segmented-control.js +57 -0
- package/dist/primitives/select.d.ts +15 -0
- package/dist/primitives/select.js +74 -0
- package/dist/primitives/sidebar-drop-zone.d.ts +21 -0
- package/dist/primitives/sidebar-drop-zone.js +88 -0
- package/dist/primitives/skeleton.d.ts +10 -0
- package/dist/primitives/skeleton.js +50 -0
- package/dist/primitives/stat-card.d.ts +16 -0
- package/dist/primitives/stat-card.js +60 -0
- package/dist/primitives/status-pill.d.ts +44 -0
- package/dist/primitives/status-pill.js +119 -0
- package/dist/primitives/switch.d.ts +6 -0
- package/dist/primitives/switch.js +16 -0
- package/dist/primitives/table.d.ts +22 -0
- package/dist/primitives/table.js +63 -0
- package/dist/primitives/tabs.d.ts +13 -0
- package/dist/{tabs-BVHR6NkO.js → primitives/tabs.js} +3 -3
- package/dist/primitives/terminal-display.d.ts +25 -0
- package/dist/primitives/terminal-display.js +109 -0
- package/dist/primitives/text-shimmer.d.ts +16 -0
- package/dist/{text-shimmer-BUUo6I-g.js → primitives/text-shimmer.js} +2 -2
- package/dist/primitives/theme-toggle.d.ts +11 -0
- package/dist/primitives/theme-toggle.js +154 -0
- package/dist/primitives/toast.d.ts +29 -0
- package/dist/primitives/toast.js +142 -0
- package/dist/primitives/toolbar.d.ts +55 -0
- package/dist/primitives/toolbar.js +37 -0
- package/dist/primitives/upload-progress.d.ts +23 -0
- package/dist/primitives/upload-progress.js +72 -0
- package/dist/primitives.d.ts +35 -5
- package/dist/primitives.js +35 -9
- package/dist/{index-COgqsFAQ.d.ts → redaction/redacted-document.d.ts} +6 -7
- package/dist/{redaction-CGGB2ENG.js → redaction/redacted-document.js} +2 -2
- package/dist/redaction.d.ts +1 -1
- package/dist/redaction.js +1 -1
- package/dist/run/expandable-content.js +41 -0
- package/dist/run/expanded-tool-detail.d.ts +12 -0
- package/dist/run/expanded-tool-detail.js +163 -0
- package/dist/run/inline-thinking-item.d.ts +11 -0
- package/dist/run/inline-thinking-item.js +60 -0
- package/dist/run/inline-tool-item.d.ts +25 -0
- package/dist/run/inline-tool-item.js +63 -0
- package/dist/run/run-group.d.ts +26 -0
- package/dist/{run-group-BWGeesgs.js → run/run-group.js} +9 -60
- package/dist/run/run-item-primitives.d.ts +5 -0
- package/dist/{run-ZsIKPA6J.js → run/run-item-primitives.js} +2 -2
- package/dist/run/run-row-shell.d.ts +53 -0
- package/dist/run/run-row-shell.js +114 -0
- package/dist/{tool-call-feed-BeB1_4pX.d.ts → run/tool-call-feed.d.ts} +7 -12
- package/dist/{tool-call-feed-BpmMLpPX.js → run/tool-call-feed.js} +4 -4
- package/dist/run/tool-call-step.d.ts +5 -0
- package/dist/run/tool-call-step.js +82 -0
- package/dist/run.d.ts +8 -2
- package/dist/run.js +7 -4
- package/dist/sdk-hooks.d.ts +8 -1
- package/dist/sdk-hooks.js +8 -2
- package/dist/stores/active-sessions-store.d.ts +83 -0
- package/dist/{active-sessions-store-Gt1R0rFG.js → stores/active-sessions-store.js} +1 -1
- package/dist/stores/chat-store.d.ts +14 -0
- package/dist/stores/chat-store.js +33 -0
- package/dist/stores.d.ts +3 -16
- package/dist/stores.js +2 -33
- package/dist/tool-previews/command-preview.d.ts +11 -0
- package/dist/tool-previews/command-preview.js +91 -0
- package/dist/tool-previews/diff-preview.d.ts +7 -0
- package/dist/tool-previews/diff-preview.js +76 -0
- package/dist/tool-previews/glob-results-preview.d.ts +7 -0
- package/dist/tool-previews/glob-results-preview.js +53 -0
- package/dist/tool-previews/grep-results-preview.d.ts +7 -0
- package/dist/tool-previews/grep-results-preview.js +99 -0
- package/dist/tool-previews/preview-primitives.js +58 -0
- package/dist/tool-previews/question-preview.d.ts +7 -0
- package/dist/tool-previews/question-preview.js +63 -0
- package/dist/tool-previews/web-search-preview.d.ts +7 -0
- package/dist/tool-previews/web-search-preview.js +78 -0
- package/dist/tool-previews/write-file-preview.d.ts +11 -0
- package/dist/tool-previews/write-file-preview.js +52 -0
- package/dist/tool-previews.d.ts +7 -1
- package/dist/tool-previews.js +7 -1
- package/dist/{branding-26d5ARIr.d.ts → types/branding.d.ts} +2 -3
- package/dist/{message-vhUI0c5G.d.ts → types/message.d.ts} +2 -3
- package/dist/{parts-5mhy9yJ3.d.ts → types/parts.d.ts} +8 -9
- package/dist/{run-OyWmIDqu.d.ts → types/run.d.ts} +9 -10
- package/dist/{tool-display-DgmL5rIU.d.ts → types/tool-display.d.ts} +5 -6
- package/dist/types.d.ts +5 -5
- package/dist/utils/copy-text.d.ts +7 -0
- package/dist/{utils-Bl_CZ0Qp.js → utils/copy-text.js} +1 -11
- package/dist/utils/format.d.ts +22 -0
- package/dist/{format-DfcLWqMa.js → utils/format.js} +1 -1
- package/dist/utils/time-ago.d.ts +3 -0
- package/dist/utils/time-ago.js +11 -0
- package/dist/utils/tool-display.d.ts +16 -0
- package/dist/{tool-display-BAuAVa43.js → utils/tool-display.js} +1 -1
- package/dist/utils.d.ts +6 -1
- package/dist/utils.js +6 -5
- package/package.json +2 -2
- package/src/files/rich-file-tree.stories.tsx +1 -1
- package/src/files/rich-file-tree.tsx +12 -9
- package/src/markdown/code-block.test.tsx +46 -2
- package/src/markdown/code-block.tsx +23 -60
- package/src/primitives/button.stories.tsx +37 -1
- package/src/primitives/button.test.tsx +230 -0
- package/src/primitives/button.tsx +62 -14
- package/src/primitives/card-anatomy.stories.tsx +51 -0
- package/src/primitives/card.test.tsx +71 -0
- package/src/primitives/card.tsx +25 -13
- package/src/primitives/heading.stories.tsx +35 -0
- package/src/primitives/heading.test.tsx +56 -0
- package/src/primitives/heading.tsx +76 -0
- package/src/primitives/index.ts +8 -0
- package/src/primitives/input.stories.tsx +28 -0
- package/src/primitives/input.test.tsx +161 -0
- package/src/primitives/input.tsx +34 -28
- package/src/primitives/page-header.stories.tsx +51 -0
- package/src/primitives/page-header.test.tsx +48 -0
- package/src/primitives/page-header.tsx +35 -35
- package/src/primitives/page-shell.stories.tsx +89 -0
- package/src/primitives/page-shell.test.tsx +24 -0
- package/src/primitives/page-shell.tsx +26 -0
- package/src/primitives/table-wrapper.stories.tsx +54 -0
- package/src/primitives/table.test.tsx +72 -0
- package/src/primitives/table.tsx +33 -12
- package/src/primitives/theme-toggle.test.tsx +167 -0
- package/src/primitives/theme-toggle.tsx +111 -39
- package/dist/active-sessions-store-nl70ZnMC.d.ts +0 -84
- package/dist/button-C87nEe2R.js +0 -78
- package/dist/chat-DDvF2oGQ.js +0 -672
- package/dist/diff-preview-BcK3nRXb.js +0 -533
- package/dist/document-editor-pane-BilJfiCo.d.ts +0 -136
- package/dist/document-editor-pane-POsq5Q0o.js +0 -1279
- package/dist/files-hywocVZz.js +0 -753
- package/dist/index-2UntyjBb.d.ts +0 -52
- package/dist/index-BgUHilZm.d.ts +0 -108
- package/dist/index-CJ8wRfiC.d.ts +0 -236
- package/dist/index-CPDET_s1.d.ts +0 -549
- package/dist/index-Cg6gYAqh.d.ts +0 -73
- package/dist/index-Cs7j8V7l.d.ts +0 -203
- package/dist/index-DjKxO357.d.ts +0 -128
- package/dist/primitives-Bx59fT-U.js +0 -1239
- package/dist/table-Bss73DhB.js +0 -146
- package/dist/tool-call-step-DNebqhmX.js +0 -434
- package/dist/use-auto-scroll-C5Pjeqon.js +0 -153
- package/dist/use-sse-stream-DjqRuDft.js +0 -884
- package/dist/use-sse-stream-Dm6R5YWz.d.ts +0 -282
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { act, cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
+
import { hydrateRoot } from "react-dom/client";
|
|
3
|
+
import { renderToString } from "react-dom/server";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
let ThemeToggle: typeof import("./theme-toggle").ThemeToggle;
|
|
7
|
+
let useTheme: typeof import("./theme-toggle").useTheme;
|
|
8
|
+
let dark = false;
|
|
9
|
+
let listeners: Set<() => void>;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
vi.resetModules();
|
|
13
|
+
for (const attr of ["class", "data-theme", "data-sandbox-theme"]) document.documentElement.removeAttribute(attr);
|
|
14
|
+
window.localStorage.clear();
|
|
15
|
+
dark = false;
|
|
16
|
+
listeners = new Set();
|
|
17
|
+
vi.stubGlobal("matchMedia", vi.fn(() => ({
|
|
18
|
+
get matches() { return dark; },
|
|
19
|
+
media: "(prefers-color-scheme: dark)",
|
|
20
|
+
addEventListener: (_: string, handler: () => void) => listeners.add(handler),
|
|
21
|
+
removeEventListener: (_: string, handler: () => void) => listeners.delete(handler),
|
|
22
|
+
})));
|
|
23
|
+
({ ThemeToggle, useTheme } = await import("./theme-toggle"));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
cleanup();
|
|
28
|
+
vi.restoreAllMocks();
|
|
29
|
+
vi.unstubAllGlobals();
|
|
30
|
+
for (const attr of ["class", "data-theme", "data-sandbox-theme"]) document.documentElement.removeAttribute(attr);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function systemMode(next: boolean) {
|
|
34
|
+
act(() => { dark = next; listeners.forEach((notify) => notify()); });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function PreferenceControl() {
|
|
38
|
+
const { theme, resolvedTheme, setTheme } = useTheme();
|
|
39
|
+
return <button onClick={() => setTheme("system")}>{theme}:{resolvedTheme}</button>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("root mode preference", () => {
|
|
43
|
+
it("tracks system changes in the DOM, icon label and click behavior", () => {
|
|
44
|
+
render(<ThemeToggle />);
|
|
45
|
+
expect(screen.getByRole("button", { name: "Switch to dark mode" })).toBeTruthy();
|
|
46
|
+
expect(document.documentElement.classList.contains("light")).toBe(true);
|
|
47
|
+
systemMode(true);
|
|
48
|
+
fireEvent.click(screen.getByRole("button", { name: "Switch to light mode" }));
|
|
49
|
+
expect(window.localStorage.getItem("theme")).toBe("light");
|
|
50
|
+
expect(document.documentElement.className).toBe("light");
|
|
51
|
+
systemMode(false); systemMode(true);
|
|
52
|
+
expect(screen.getByRole("button", { name: "Switch to dark mode" })).toBeTruthy();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("shares explicit choices and a return to system across mounted consumers", () => {
|
|
56
|
+
window.localStorage.setItem("theme", "dark");
|
|
57
|
+
render(<><ThemeToggle /><PreferenceControl /></>);
|
|
58
|
+
fireEvent.click(screen.getByRole("button", { name: "Switch to light mode" }));
|
|
59
|
+
expect(screen.getByRole("button", { name: "light:light" })).toBeTruthy();
|
|
60
|
+
fireEvent.click(screen.getByRole("button", { name: "light:light" }));
|
|
61
|
+
expect(window.localStorage.getItem("theme")).toBeNull();
|
|
62
|
+
systemMode(true);
|
|
63
|
+
expect(screen.getByRole("button", { name: "system:dark" })).toBeTruthy();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("remains usable when storage reads, writes and removals throw", () => {
|
|
67
|
+
for (const method of ["getItem", "setItem", "removeItem"] as const) {
|
|
68
|
+
vi.spyOn(Storage.prototype, method).mockImplementation(() => { throw new Error("denied"); });
|
|
69
|
+
}
|
|
70
|
+
render(<><ThemeToggle /><PreferenceControl /></>);
|
|
71
|
+
fireEvent.click(screen.getByRole("button", { name: "Switch to dark mode" }));
|
|
72
|
+
expect(screen.getByRole("button", { name: "dark:dark" })).toBeTruthy();
|
|
73
|
+
fireEvent.click(screen.getByRole("button", { name: "dark:dark" }));
|
|
74
|
+
expect(screen.getByRole("button", { name: "system:light" })).toBeTruthy();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("ignores invalid stored values and handles cross-tab preference changes", () => {
|
|
78
|
+
window.localStorage.setItem("theme", "not-a-theme");
|
|
79
|
+
render(<ThemeToggle />);
|
|
80
|
+
expect(document.documentElement.className).toBe("light");
|
|
81
|
+
act(() => window.dispatchEvent(new StorageEvent("storage", { key: "theme", newValue: "dark" })));
|
|
82
|
+
expect(screen.getByRole("button", { name: "Switch to light mode" })).toBeTruthy();
|
|
83
|
+
act(() => window.dispatchEvent(new StorageEvent("storage", { key: "theme", newValue: null })));
|
|
84
|
+
expect(document.documentElement.className).toBe("light");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it.each(["light", "dark"])("honors explicit data-theme=%s and keeps it in sync", (mode) => {
|
|
88
|
+
document.documentElement.dataset.theme = mode;
|
|
89
|
+
dark = mode !== "dark";
|
|
90
|
+
render(<ThemeToggle />);
|
|
91
|
+
expect(document.documentElement.className).toBe(mode);
|
|
92
|
+
fireEvent.click(screen.getByRole("button"));
|
|
93
|
+
expect(document.documentElement.dataset.theme).toBe(mode === "light" ? "dark" : "light");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it.each(["light", "dark"])("honors an explicit .%s class without a saved preference", (mode) => {
|
|
97
|
+
document.documentElement.className = mode;
|
|
98
|
+
dark = mode !== "dark";
|
|
99
|
+
render(<ThemeToggle />);
|
|
100
|
+
expect(document.documentElement.className).toBe(mode);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it.each([
|
|
104
|
+
["aubergine", "aubergine-light"], ["arena", "arena-light"], ["tangle-dark", "tangle-light"],
|
|
105
|
+
])("keeps the %s named family in both directions", (name, lightName) => {
|
|
106
|
+
document.documentElement.dataset.theme = name;
|
|
107
|
+
render(<ThemeToggle />);
|
|
108
|
+
fireEvent.click(screen.getByRole("button", { name: "Switch to light mode" }));
|
|
109
|
+
expect(document.documentElement.dataset.theme).toBe(lightName);
|
|
110
|
+
fireEvent.click(screen.getByRole("button", { name: "Switch to dark mode" }));
|
|
111
|
+
expect(document.documentElement.dataset.theme).toBe(name);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it.each(["intelligence", "custom-product"])("does not overwrite unpaired identity %s", (name) => {
|
|
115
|
+
document.documentElement.dataset.theme = name;
|
|
116
|
+
document.documentElement.className = "dark";
|
|
117
|
+
render(<ThemeToggle />);
|
|
118
|
+
fireEvent.click(screen.getByRole("button"));
|
|
119
|
+
expect(document.documentElement.dataset.theme).toBe(name);
|
|
120
|
+
expect(document.documentElement.className).toBe("light");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it.each(["vault", "dawn"])("preserves legacy %s identity while changing mode", (name) => {
|
|
124
|
+
document.documentElement.dataset.sandboxTheme = name;
|
|
125
|
+
render(<ThemeToggle />);
|
|
126
|
+
fireEvent.click(screen.getByRole("button", { name: "Switch to dark mode" }));
|
|
127
|
+
expect(document.documentElement.dataset.sandboxTheme).toBe(name);
|
|
128
|
+
expect(document.documentElement.className).toBe("dark");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("uses the dark fallback without matchMedia and cleans subscriptions on unmount", () => {
|
|
132
|
+
const view = render(<ThemeToggle />);
|
|
133
|
+
expect(listeners.size).toBe(1);
|
|
134
|
+
view.unmount();
|
|
135
|
+
expect(listeners.size).toBe(0);
|
|
136
|
+
vi.stubGlobal("matchMedia", undefined);
|
|
137
|
+
render(<ThemeToggle />);
|
|
138
|
+
expect(screen.getByRole("button", { name: "Switch to light mode" })).toBeTruthy();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("server rendering and hydration", () => {
|
|
143
|
+
it("renders without window, document, matchMedia or storage", () => {
|
|
144
|
+
vi.stubGlobal("window", undefined);
|
|
145
|
+
vi.stubGlobal("document", undefined);
|
|
146
|
+
expect(renderToString(<ThemeToggle />)).toContain("Switch to light mode");
|
|
147
|
+
vi.unstubAllGlobals();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("hydrates the server snapshot then resolves saved light without a dark write", async () => {
|
|
151
|
+
const html = renderToString(<ThemeToggle />);
|
|
152
|
+
window.localStorage.setItem("theme", "light");
|
|
153
|
+
document.documentElement.className = "light";
|
|
154
|
+
const host = document.createElement("div");
|
|
155
|
+
host.innerHTML = html;
|
|
156
|
+
document.body.append(host);
|
|
157
|
+
const recoverable = vi.fn();
|
|
158
|
+
const toggle = vi.spyOn(document.documentElement.classList, "toggle");
|
|
159
|
+
let root: ReturnType<typeof hydrateRoot>;
|
|
160
|
+
await act(async () => { root = hydrateRoot(host, <ThemeToggle />, { onRecoverableError: recoverable }); });
|
|
161
|
+
expect(host.querySelector("button")?.getAttribute("aria-label")).toBe("Switch to dark mode");
|
|
162
|
+
expect(recoverable).not.toHaveBeenCalled();
|
|
163
|
+
expect(toggle).not.toHaveBeenCalledWith("dark", true);
|
|
164
|
+
await act(async () => root.unmount());
|
|
165
|
+
host.remove();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -1,44 +1,117 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useSyncExternalStore } from "react";
|
|
2
2
|
|
|
3
3
|
type Theme = "light" | "dark" | "system";
|
|
4
|
+
type Mode = Exclude<Theme, "system">;
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const preferenceEvent = "tangle:theme-preference";
|
|
7
|
+
const systemQuery = "(prefers-color-scheme: dark)";
|
|
8
|
+
// Only existing, paired Brand themes. Never turn an arbitrary product identity
|
|
9
|
+
// (notably Intelligence) into data-theme="light" or data-theme="dark".
|
|
10
|
+
const namedModes = [
|
|
11
|
+
{ dark: "aubergine", light: "aubergine-light" },
|
|
12
|
+
{ dark: "arena", light: "arena-light" },
|
|
13
|
+
{ dark: "tangle-dark", light: "tangle-light" },
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
// Client-only preference cache also keeps the control usable when storage is
|
|
17
|
+
// denied, and lets multiple useTheme consumers share the same preference.
|
|
18
|
+
// Server renders ONLY read getServerTheme; no request state is stored here.
|
|
19
|
+
let preference: Theme | undefined;
|
|
20
|
+
|
|
21
|
+
function isTheme(value: unknown): value is Theme {
|
|
22
|
+
return value === "light" || value === "dark" || value === "system";
|
|
9
23
|
}
|
|
10
24
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
25
|
+
function getSystemTheme(): Mode {
|
|
26
|
+
if (typeof window === "undefined" || !window.matchMedia) return "dark";
|
|
27
|
+
return window.matchMedia(systemQuery).matches ? "dark" : "light";
|
|
14
28
|
}
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
function getServerTheme(): Theme { return "system"; }
|
|
31
|
+
function getServerMode(): Mode { return "dark"; }
|
|
32
|
+
|
|
33
|
+
function readInitialTheme(): Theme {
|
|
34
|
+
try {
|
|
35
|
+
const stored = window.localStorage.getItem("theme");
|
|
36
|
+
if (isTheme(stored)) return stored;
|
|
37
|
+
} catch { /* Storage is optional, not a prerequisite for changing mode. */ }
|
|
38
|
+
|
|
39
|
+
const root = document.documentElement;
|
|
40
|
+
const named = root.getAttribute("data-theme");
|
|
41
|
+
if (named === "light" || named === "dark") return named;
|
|
42
|
+
for (const pair of namedModes) {
|
|
43
|
+
if (named === pair.light) return "light";
|
|
44
|
+
if (named === pair.dark) return "dark";
|
|
45
|
+
}
|
|
46
|
+
if (root.classList.contains("light")) return "light";
|
|
47
|
+
if (root.classList.contains("dark")) return "dark";
|
|
48
|
+
const legacy = root.getAttribute("data-sandbox-theme");
|
|
49
|
+
if (legacy === "vault" || legacy === "dawn") return "light";
|
|
50
|
+
return "system";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getTheme(): Theme {
|
|
54
|
+
if (typeof window === "undefined") return "system";
|
|
55
|
+
return preference ??= readInitialTheme();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function applyMode(mode: Mode) {
|
|
59
|
+
const root = document.documentElement;
|
|
60
|
+
root.classList.toggle("dark", mode === "dark");
|
|
61
|
+
root.classList.toggle("light", mode === "light");
|
|
62
|
+
const named = root.getAttribute("data-theme");
|
|
63
|
+
if (named === "dark" || named === "light") {
|
|
64
|
+
root.setAttribute("data-theme", mode);
|
|
65
|
+
} else {
|
|
66
|
+
const pair = namedModes.find((pair) => named === pair.dark || named === pair.light);
|
|
67
|
+
if (pair) root.setAttribute("data-theme", pair[mode]);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function setTheme(next: Theme) {
|
|
72
|
+
if (!isTheme(next) || typeof window === "undefined") return;
|
|
73
|
+
preference = next;
|
|
74
|
+
try {
|
|
75
|
+
if (next === "system") window.localStorage.removeItem("theme");
|
|
76
|
+
else window.localStorage.setItem("theme", next);
|
|
77
|
+
} catch { /* Keep the in-memory preference when storage is unavailable. */ }
|
|
78
|
+
applyMode(next === "system" ? getSystemTheme() : next);
|
|
79
|
+
window.dispatchEvent(new Event(preferenceEvent));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function subscribeTheme(notify: () => void) {
|
|
83
|
+
const onStorage = (event: StorageEvent) => {
|
|
84
|
+
if (event.key !== "theme" && event.key !== null) return;
|
|
85
|
+
preference = isTheme(event.newValue) ? event.newValue : "system";
|
|
86
|
+
notify();
|
|
87
|
+
};
|
|
88
|
+
window.addEventListener(preferenceEvent, notify);
|
|
89
|
+
window.addEventListener("storage", onStorage);
|
|
90
|
+
return () => {
|
|
91
|
+
window.removeEventListener(preferenceEvent, notify);
|
|
92
|
+
window.removeEventListener("storage", onStorage);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
31
95
|
|
|
96
|
+
function subscribeSystem(notify: () => void) {
|
|
97
|
+
if (!window.matchMedia) return () => {};
|
|
98
|
+
const media = window.matchMedia(systemQuery);
|
|
99
|
+
media.addEventListener("change", notify);
|
|
100
|
+
return () => media.removeEventListener("change", notify);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function useTheme() {
|
|
104
|
+
const theme = useSyncExternalStore(subscribeTheme, getTheme, getServerTheme);
|
|
105
|
+
const system = useSyncExternalStore(subscribeSystem, getSystemTheme, getServerMode);
|
|
106
|
+
const resolvedTheme = theme === "system" ? system : theme;
|
|
32
107
|
useEffect(() => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return { theme, setTheme };
|
|
108
|
+
// Hydration first renders the stable server snapshot. Apply the real client
|
|
109
|
+
// preference, not that placeholder, so we never repaint a persisted light
|
|
110
|
+
// page dark while React is catching up.
|
|
111
|
+
const selected = getTheme();
|
|
112
|
+
applyMode(selected === "system" ? getSystemTheme() : selected);
|
|
113
|
+
}, [resolvedTheme]);
|
|
114
|
+
return { theme, setTheme, resolvedTheme };
|
|
42
115
|
}
|
|
43
116
|
|
|
44
117
|
const iconClass = "h-4 w-4";
|
|
@@ -54,6 +127,7 @@ function SunIcon() {
|
|
|
54
127
|
strokeLinecap="round"
|
|
55
128
|
strokeLinejoin="round"
|
|
56
129
|
className={iconClass}
|
|
130
|
+
aria-hidden="true"
|
|
57
131
|
>
|
|
58
132
|
<circle cx={12} cy={12} r={5} />
|
|
59
133
|
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" />
|
|
@@ -72,6 +146,7 @@ function MoonIcon() {
|
|
|
72
146
|
strokeLinecap="round"
|
|
73
147
|
strokeLinejoin="round"
|
|
74
148
|
className={iconClass}
|
|
149
|
+
aria-hidden="true"
|
|
75
150
|
>
|
|
76
151
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
77
152
|
</svg>
|
|
@@ -79,18 +154,15 @@ function MoonIcon() {
|
|
|
79
154
|
}
|
|
80
155
|
|
|
81
156
|
export function ThemeToggle() {
|
|
82
|
-
const {
|
|
83
|
-
const resolved =
|
|
84
|
-
theme === "system" ? getSystemTheme() : theme;
|
|
85
|
-
|
|
157
|
+
const { resolvedTheme, setTheme } = useTheme();
|
|
86
158
|
return (
|
|
87
159
|
<button
|
|
88
160
|
type="button"
|
|
89
|
-
onClick={() => setTheme(
|
|
161
|
+
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
|
|
90
162
|
className="inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors"
|
|
91
|
-
aria-label={`Switch to ${
|
|
163
|
+
aria-label={`Switch to ${resolvedTheme === "dark" ? "light" : "dark"} mode`}
|
|
92
164
|
>
|
|
93
|
-
{
|
|
165
|
+
{resolvedTheme === "dark" ? <SunIcon /> : <MoonIcon />}
|
|
94
166
|
</button>
|
|
95
167
|
);
|
|
96
168
|
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
//#region src/stores/active-sessions-store.d.ts
|
|
2
|
-
type SessionProjectKey = string | number;
|
|
3
|
-
type ActiveSessionStatus = "idle" | "running" | "attention-needed" | "error";
|
|
4
|
-
type ActiveSessionReconnectState = "idle" | "reconnecting" | "failed";
|
|
5
|
-
type ActiveSessionConnectionState = "disconnected" | "connecting" | "connected" | "reconnecting" | "error";
|
|
6
|
-
type ActiveSessionTransportMode = "websocket" | "sse" | "polling" | "custom";
|
|
7
|
-
interface ActiveSessionRecord {
|
|
8
|
-
sessionId: string;
|
|
9
|
-
projectId: SessionProjectKey | null;
|
|
10
|
-
projectLabel?: string;
|
|
11
|
-
title?: string;
|
|
12
|
-
href?: string;
|
|
13
|
-
registeredAt: number;
|
|
14
|
-
lastActivityAt: number;
|
|
15
|
-
lastEventAt: number | null;
|
|
16
|
-
status: ActiveSessionStatus;
|
|
17
|
-
isRunning: boolean;
|
|
18
|
-
isForeground: boolean;
|
|
19
|
-
needsAttention: boolean;
|
|
20
|
-
connectionState: ActiveSessionConnectionState;
|
|
21
|
-
reconnectState: ActiveSessionReconnectState;
|
|
22
|
-
transportMode: ActiveSessionTransportMode | null;
|
|
23
|
-
lastError: string | null;
|
|
24
|
-
metadata?: Record<string, unknown>;
|
|
25
|
-
}
|
|
26
|
-
interface ActiveSessionsState {
|
|
27
|
-
sessions: Record<string, ActiveSessionRecord>;
|
|
28
|
-
lastUpdatedAt: number;
|
|
29
|
-
}
|
|
30
|
-
interface RegisterActiveSessionOptions {
|
|
31
|
-
sessionId: string;
|
|
32
|
-
projectId?: SessionProjectKey | null;
|
|
33
|
-
projectLabel?: string;
|
|
34
|
-
title?: string;
|
|
35
|
-
href?: string;
|
|
36
|
-
metadata?: Record<string, unknown>;
|
|
37
|
-
}
|
|
38
|
-
interface ActiveSessionConnectionOptions {
|
|
39
|
-
connectionState: ActiveSessionConnectionState;
|
|
40
|
-
reconnectState?: ActiveSessionReconnectState;
|
|
41
|
-
transportMode?: ActiveSessionTransportMode | null;
|
|
42
|
-
lastError?: string | null;
|
|
43
|
-
lastEventAt?: number | null;
|
|
44
|
-
}
|
|
45
|
-
interface ActiveSessionActivityOptions {
|
|
46
|
-
lastEventAt?: number | null;
|
|
47
|
-
}
|
|
48
|
-
interface ActiveProjectActivity {
|
|
49
|
-
projectId: SessionProjectKey;
|
|
50
|
-
projectLabel?: string;
|
|
51
|
-
activeSessionCount: number;
|
|
52
|
-
runningSessionIds: string[];
|
|
53
|
-
lastActivityAt: number;
|
|
54
|
-
}
|
|
55
|
-
declare const activeSessionsAtom: import("nanostores").PreinitializedWritableAtom<ActiveSessionsState> & object;
|
|
56
|
-
declare function registerActiveSession(options: RegisterActiveSessionOptions): void;
|
|
57
|
-
declare function unregisterActiveSession(sessionId: string): void;
|
|
58
|
-
declare function setForegroundActiveSession(sessionId: string | null): void;
|
|
59
|
-
declare function updateActiveSessionMeta(sessionId: string, meta: Partial<Pick<ActiveSessionRecord, "title" | "href" | "projectId" | "projectLabel" | "metadata">>): void;
|
|
60
|
-
declare function setActiveSessionConnection(sessionId: string, options: ActiveSessionConnectionOptions): void;
|
|
61
|
-
declare function setActiveSessionRunning(sessionId: string, isRunning: boolean, options?: ActiveSessionActivityOptions): void;
|
|
62
|
-
declare function setActiveSessionAttention(sessionId: string, needsAttention: boolean, options?: ActiveSessionActivityOptions): void;
|
|
63
|
-
declare function setActiveSessionError(sessionId: string, error: string | null): void;
|
|
64
|
-
declare function bumpActiveSessionActivity(sessionId: string, options?: ActiveSessionActivityOptions): void;
|
|
65
|
-
declare function resetActiveSessions(): void;
|
|
66
|
-
declare function getAllActiveSessions(state: ActiveSessionsState): ActiveSessionRecord[];
|
|
67
|
-
declare function getActiveSession(state: ActiveSessionsState, sessionId: string): ActiveSessionRecord | null;
|
|
68
|
-
declare function getSessionsForProject(state: ActiveSessionsState, projectId: SessionProjectKey): ActiveSessionRecord[];
|
|
69
|
-
declare function getSessionsForNavbar(state: ActiveSessionsState, projectId?: SessionProjectKey | null): ActiveSessionRecord[];
|
|
70
|
-
declare function getSessionsByActivity(state: ActiveSessionsState): ActiveSessionRecord[];
|
|
71
|
-
declare function getTotalRunningSessionCount(state: ActiveSessionsState): number;
|
|
72
|
-
declare function hasBackgroundRunningSessions(state: ActiveSessionsState): boolean;
|
|
73
|
-
declare function getAllProjectActivity(state: ActiveSessionsState): ActiveProjectActivity[];
|
|
74
|
-
declare function useActiveSessionsState(): ActiveSessionsState;
|
|
75
|
-
declare function useActiveSessions(): ActiveSessionRecord[];
|
|
76
|
-
declare function useActiveSession(sessionId: string | null): ActiveSessionRecord | null;
|
|
77
|
-
declare function useProjectSessions(projectId: SessionProjectKey | null): ActiveSessionRecord[];
|
|
78
|
-
declare function useNavbarSessions(projectId?: SessionProjectKey | null): ActiveSessionRecord[];
|
|
79
|
-
declare function useSessionsByActivity(): ActiveSessionRecord[];
|
|
80
|
-
declare function useProjectActivity(): ActiveProjectActivity[];
|
|
81
|
-
declare function useTotalRunningSessions(): number;
|
|
82
|
-
declare function useHasBackgroundRunningSessions(): boolean;
|
|
83
|
-
//#endregion
|
|
84
|
-
export { updateActiveSessionMeta as A, resetActiveSessions as C, setActiveSessionRunning as D, setActiveSessionError as E, useNavbarSessions as F, useProjectActivity as I, useProjectSessions as L, useActiveSessions as M, useActiveSessionsState as N, setForegroundActiveSession as O, useHasBackgroundRunningSessions as P, useSessionsByActivity as R, registerActiveSession as S, setActiveSessionConnection as T, getSessionsByActivity as _, ActiveSessionReconnectState as a, getTotalRunningSessionCount as b, ActiveSessionTransportMode as c, SessionProjectKey as d, activeSessionsAtom as f, getAllProjectActivity as g, getAllActiveSessions as h, ActiveSessionConnectionState as i, useActiveSession as j, unregisterActiveSession as k, ActiveSessionsState as l, getActiveSession as m, ActiveSessionActivityOptions as n, ActiveSessionRecord as o, bumpActiveSessionActivity as p, ActiveSessionConnectionOptions as r, ActiveSessionStatus as s, ActiveProjectActivity as t, RegisterActiveSessionOptions as u, getSessionsForNavbar as v, setActiveSessionAttention as w, hasBackgroundRunningSessions as x, getSessionsForProject as y, useTotalRunningSessions as z };
|
package/dist/button-C87nEe2R.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { i as focusRing } from "./focus-BcE_SHqz.js";
|
|
2
|
-
import { t as cn } from "./utils-C_uf36nf.js";
|
|
3
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
-
import { cva } from "class-variance-authority";
|
|
5
|
-
import * as React from "react";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
//#region src/primitives/button.tsx
|
|
8
|
-
const buttonVariants = cva(`inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium text-sm transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`, {
|
|
9
|
-
variants: {
|
|
10
|
-
variant: {
|
|
11
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.97] duration-[var(--transition-fast)]",
|
|
12
|
-
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.97] duration-[var(--transition-fast)]",
|
|
13
|
-
outline: "border border-border bg-card hover:bg-muted active:scale-[0.97] duration-[var(--transition-fast)] text-foreground",
|
|
14
|
-
secondary: "bg-muted border border-border text-foreground hover:bg-muted/80 active:scale-[0.97] duration-[var(--transition-fast)]",
|
|
15
|
-
ghost: "hover:bg-muted hover:text-foreground duration-[var(--transition-fast)] text-muted-foreground border border-transparent",
|
|
16
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
17
|
-
sandbox: "bg-[var(--btn-primary-bg)] text-[var(--btn-primary-text)] border border-[var(--border-accent)] hover:bg-[var(--btn-primary-hover)] active:scale-[0.97] duration-[var(--transition-fast)]"
|
|
18
|
-
},
|
|
19
|
-
size: {
|
|
20
|
-
default: "h-[var(--control-height)] px-4 py-2",
|
|
21
|
-
sm: "h-8 rounded-md px-3 text-xs",
|
|
22
|
-
lg: "h-11 rounded-lg px-7 text-sm",
|
|
23
|
-
xl: "h-13 rounded-xl px-9 text-base",
|
|
24
|
-
icon: "h-[var(--control-height)] w-[var(--control-height)]"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
defaultVariants: {
|
|
28
|
-
variant: "default",
|
|
29
|
-
size: "default"
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
const Button = React.forwardRef(({ className, variant, size, asChild = false, loading = false, disabled, children, ...props }, ref) => {
|
|
33
|
-
const Comp = asChild ? Slot : "button";
|
|
34
|
-
if (asChild) return /* @__PURE__ */ jsx(Comp, {
|
|
35
|
-
className: cn(buttonVariants({
|
|
36
|
-
variant,
|
|
37
|
-
size,
|
|
38
|
-
className
|
|
39
|
-
})),
|
|
40
|
-
ref,
|
|
41
|
-
...props,
|
|
42
|
-
children
|
|
43
|
-
});
|
|
44
|
-
return /* @__PURE__ */ jsxs(Comp, {
|
|
45
|
-
className: cn(buttonVariants({
|
|
46
|
-
variant,
|
|
47
|
-
size,
|
|
48
|
-
className
|
|
49
|
-
})),
|
|
50
|
-
ref,
|
|
51
|
-
disabled: disabled || loading,
|
|
52
|
-
...props,
|
|
53
|
-
children: [loading && /* @__PURE__ */ jsxs("svg", {
|
|
54
|
-
className: "mr-2 -ml-1 h-4 w-4 animate-spin",
|
|
55
|
-
fill: "none",
|
|
56
|
-
viewBox: "0 0 24 24",
|
|
57
|
-
children: [
|
|
58
|
-
/* @__PURE__ */ jsx("title", { children: "Loading spinner" }),
|
|
59
|
-
/* @__PURE__ */ jsx("circle", {
|
|
60
|
-
className: "opacity-25",
|
|
61
|
-
cx: "12",
|
|
62
|
-
cy: "12",
|
|
63
|
-
r: "10",
|
|
64
|
-
stroke: "currentColor",
|
|
65
|
-
strokeWidth: "4"
|
|
66
|
-
}),
|
|
67
|
-
/* @__PURE__ */ jsx("path", {
|
|
68
|
-
className: "opacity-75",
|
|
69
|
-
fill: "currentColor",
|
|
70
|
-
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
71
|
-
})
|
|
72
|
-
]
|
|
73
|
-
}), children]
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
Button.displayName = "Button";
|
|
77
|
-
//#endregion
|
|
78
|
-
export { buttonVariants as n, Button as t };
|