@tangle-network/ui 11.12.0 → 11.13.1

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.
Files changed (276) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/_virtual/_rolldown/runtime.js +13 -0
  3. package/dist/auth/auth.d.ts +53 -0
  4. package/dist/{auth-DOETfnd9.js → auth/auth.js} +7 -37
  5. package/dist/auth/login-layout.d.ts +20 -0
  6. package/dist/auth/login-layout.js +36 -0
  7. package/dist/auth.d.ts +2 -1
  8. package/dist/auth.js +2 -1
  9. package/dist/chat/agent-timeline.d.ts +83 -0
  10. package/dist/chat/agent-timeline.js +236 -0
  11. package/dist/chat/chat-container.d.ts +51 -0
  12. package/dist/chat/chat-container.js +309 -0
  13. package/dist/chat/chat-message.d.ts +22 -0
  14. package/dist/chat/chat-message.js +38 -0
  15. package/dist/chat/message-list.d.ts +32 -0
  16. package/dist/chat/message-list.js +37 -0
  17. package/dist/chat/thinking-indicator.d.ts +6 -0
  18. package/dist/chat/thinking-indicator.js +38 -0
  19. package/dist/chat/user-message.d.ts +21 -0
  20. package/dist/chat/user-message.js +42 -0
  21. package/dist/chat.d.ts +6 -1
  22. package/dist/chat.js +6 -1
  23. package/dist/editor/document-editor-pane.d.ts +37 -0
  24. package/dist/editor/document-editor-pane.js +199 -0
  25. package/dist/editor/editor-lazy.js +37 -0
  26. package/dist/editor/editor-loading.js +21 -0
  27. package/dist/editor/editor-peers.d.ts +6 -0
  28. package/dist/editor/editor-peers.js +155 -0
  29. package/dist/editor/editor-provider.d.ts +97 -0
  30. package/dist/editor/editor-provider.js +253 -0
  31. package/dist/editor/editor-toolbar.d.ts +11 -0
  32. package/dist/editor/editor-toolbar.js +106 -0
  33. package/dist/editor/markdown-conversion.js +23 -0
  34. package/dist/editor/markdown-document-editor.js +103 -0
  35. package/dist/editor/tiptap-editor.d.ts +45 -0
  36. package/dist/editor/tiptap-editor.js +271 -0
  37. package/dist/editor/use-editor.d.ts +67 -0
  38. package/dist/editor/use-editor.js +158 -0
  39. package/dist/editor.d.ts +6 -119
  40. package/dist/editor.js +5 -1
  41. package/dist/files/file-artifact-pane.d.ts +47 -0
  42. package/dist/files/file-artifact-pane.js +114 -0
  43. package/dist/files/file-format.d.ts +51 -0
  44. package/dist/{file-format-BgXbr-Ib.js → files/file-format.js} +1 -1
  45. package/dist/files/file-preview.d.ts +27 -0
  46. package/dist/files/file-preview.js +359 -0
  47. package/dist/files/file-tabs.d.ts +19 -0
  48. package/dist/files/file-tabs.js +48 -0
  49. package/dist/files/file-tree.d.ts +31 -0
  50. package/dist/files/file-tree.js +135 -0
  51. package/dist/files/rich-file-tree.d.ts +62 -0
  52. package/dist/files/rich-file-tree.js +122 -0
  53. package/dist/files.d.ts +6 -1
  54. package/dist/files.js +6 -2
  55. package/dist/hooks/use-auth.d.ts +40 -0
  56. package/dist/hooks/use-auth.js +91 -0
  57. package/dist/hooks/use-auto-scroll.d.ts +11 -0
  58. package/dist/hooks/use-auto-scroll.js +48 -0
  59. package/dist/hooks/use-dropdown-menu.d.ts +12 -0
  60. package/dist/hooks/use-dropdown-menu.js +31 -0
  61. package/dist/hooks/use-live-time.d.ts +7 -0
  62. package/dist/hooks/use-live-time.js +22 -0
  63. package/dist/hooks/use-realtime-session.d.ts +32 -0
  64. package/dist/hooks/use-realtime-session.js +235 -0
  65. package/dist/hooks/use-run-collapse-state.d.ts +13 -0
  66. package/dist/hooks/use-run-collapse-state.js +24 -0
  67. package/dist/hooks/use-run-groups.d.ts +11 -0
  68. package/dist/hooks/use-run-groups.js +85 -0
  69. package/dist/hooks/use-sdk-session.d.ts +59 -0
  70. package/dist/hooks/use-sdk-session.js +322 -0
  71. package/dist/hooks/use-sse-stream.d.ts +124 -0
  72. package/dist/hooks/use-sse-stream.js +229 -0
  73. package/dist/hooks/use-tool-call-stream.d.ts +23 -0
  74. package/dist/hooks/use-tool-call-stream.js +75 -0
  75. package/dist/hooks.d.ts +11 -49
  76. package/dist/hooks.js +10 -110
  77. package/dist/index.d.ts +99 -25
  78. package/dist/index.js +86 -36
  79. package/dist/lib/focus.d.ts +55 -0
  80. package/dist/{focus-BcE_SHqz.js → lib/focus.js} +1 -1
  81. package/dist/lib/utils.d.ts +4 -0
  82. package/dist/{utils-C_uf36nf.js → lib/utils.js} +1 -1
  83. package/dist/{code-block-B9miUPht.d.ts → markdown/code-block.d.ts} +5 -6
  84. package/dist/{code-block-B36KVUSN.js → markdown/code-block.js} +60 -55
  85. package/dist/{markdown-C0atSabr.d.ts → markdown/markdown.d.ts} +4 -5
  86. package/dist/{markdown-Db08KAYF.js → markdown/markdown.js} +3 -3
  87. package/dist/markdown.d.ts +2 -2
  88. package/dist/markdown.js +2 -2
  89. package/dist/{openui-artifact-renderer-fkBQaoLO.d.ts → openui/openui-artifact-renderer.d.ts} +20 -21
  90. package/dist/{openui-artifact-renderer-DG_i2lOR.js → openui/openui-artifact-renderer.js} +9 -7
  91. package/dist/{schema-BpW9rSSG.js → openui/schema-compiled.js} +2 -354
  92. package/dist/{schema-DAstk-J4.d.ts → openui/schema-data.d.ts} +3 -24
  93. package/dist/openui/schema-data.js +206 -0
  94. package/dist/openui-schema.d.ts +22 -3
  95. package/dist/openui-schema.js +138 -1
  96. package/dist/openui.d.ts +3 -2
  97. package/dist/openui.js +3 -2
  98. package/dist/{artifact-pane-BL_DFWNl.d.ts → primitives/artifact-pane.d.ts} +3 -4
  99. package/dist/{artifact-pane-DMXT9a8U.js → primitives/artifact-pane.js} +2 -2
  100. package/dist/primitives/avatar.d.ts +8 -0
  101. package/dist/primitives/avatar.js +25 -0
  102. package/dist/primitives/badge.d.ts +22 -0
  103. package/dist/primitives/badge.js +47 -0
  104. package/dist/{button-yoIv77e2.d.ts → primitives/button.d.ts} +2 -2
  105. package/dist/primitives/button.js +113 -0
  106. package/dist/primitives/card.d.ts +18 -0
  107. package/dist/primitives/card.js +52 -0
  108. package/dist/primitives/code-block.d.ts +6 -0
  109. package/dist/primitives/code-block.js +13 -0
  110. package/dist/primitives/dialog.d.ts +23 -0
  111. package/dist/primitives/dialog.js +59 -0
  112. package/dist/primitives/drop-zone.d.ts +23 -0
  113. package/dist/primitives/drop-zone.js +104 -0
  114. package/dist/primitives/dropdown-menu.d.ts +29 -0
  115. package/dist/{dropdown-menu-BpcLa5vF.js → primitives/dropdown-menu.js} +2 -23
  116. package/dist/primitives/empty-state.d.ts +11 -0
  117. package/dist/primitives/empty-state.js +32 -0
  118. package/dist/primitives/heading.d.ts +23 -0
  119. package/dist/primitives/heading.js +38 -0
  120. package/dist/primitives/input.d.ts +23 -0
  121. package/dist/primitives/input.js +110 -0
  122. package/dist/primitives/label.d.ts +6 -0
  123. package/dist/primitives/label.js +16 -0
  124. package/dist/primitives/metric-strip.d.ts +48 -0
  125. package/dist/primitives/metric-strip.js +52 -0
  126. package/dist/primitives/page-header.d.ts +25 -0
  127. package/dist/primitives/page-header.js +47 -0
  128. package/dist/primitives/page-shell.d.ts +14 -0
  129. package/dist/primitives/page-shell.js +17 -0
  130. package/dist/primitives/progress.d.ts +9 -0
  131. package/dist/primitives/progress.js +29 -0
  132. package/dist/primitives/segmented-control.d.ts +40 -0
  133. package/dist/primitives/segmented-control.js +57 -0
  134. package/dist/primitives/select.d.ts +15 -0
  135. package/dist/primitives/select.js +74 -0
  136. package/dist/primitives/sidebar-drop-zone.d.ts +21 -0
  137. package/dist/primitives/sidebar-drop-zone.js +88 -0
  138. package/dist/primitives/skeleton.d.ts +10 -0
  139. package/dist/primitives/skeleton.js +50 -0
  140. package/dist/primitives/stat-card.d.ts +16 -0
  141. package/dist/primitives/stat-card.js +60 -0
  142. package/dist/primitives/status-pill.d.ts +44 -0
  143. package/dist/primitives/status-pill.js +119 -0
  144. package/dist/primitives/switch.d.ts +6 -0
  145. package/dist/primitives/switch.js +16 -0
  146. package/dist/primitives/table.d.ts +22 -0
  147. package/dist/primitives/table.js +63 -0
  148. package/dist/primitives/tabs.d.ts +13 -0
  149. package/dist/{tabs-BVHR6NkO.js → primitives/tabs.js} +3 -3
  150. package/dist/primitives/terminal-display.d.ts +25 -0
  151. package/dist/primitives/terminal-display.js +109 -0
  152. package/dist/primitives/text-shimmer.d.ts +16 -0
  153. package/dist/{text-shimmer-BUUo6I-g.js → primitives/text-shimmer.js} +2 -2
  154. package/dist/primitives/theme-toggle.d.ts +11 -0
  155. package/dist/primitives/theme-toggle.js +154 -0
  156. package/dist/primitives/toast.d.ts +29 -0
  157. package/dist/primitives/toast.js +142 -0
  158. package/dist/primitives/toolbar.d.ts +55 -0
  159. package/dist/primitives/toolbar.js +37 -0
  160. package/dist/primitives/upload-progress.d.ts +23 -0
  161. package/dist/primitives/upload-progress.js +72 -0
  162. package/dist/primitives.d.ts +35 -5
  163. package/dist/primitives.js +35 -9
  164. package/dist/{index-COgqsFAQ.d.ts → redaction/redacted-document.d.ts} +6 -7
  165. package/dist/{redaction-CGGB2ENG.js → redaction/redacted-document.js} +2 -2
  166. package/dist/redaction.d.ts +1 -1
  167. package/dist/redaction.js +1 -1
  168. package/dist/run/expandable-content.js +41 -0
  169. package/dist/run/expanded-tool-detail.d.ts +12 -0
  170. package/dist/run/expanded-tool-detail.js +163 -0
  171. package/dist/run/inline-thinking-item.d.ts +11 -0
  172. package/dist/run/inline-thinking-item.js +60 -0
  173. package/dist/run/inline-tool-item.d.ts +25 -0
  174. package/dist/run/inline-tool-item.js +63 -0
  175. package/dist/run/run-group.d.ts +26 -0
  176. package/dist/{run-group-BWGeesgs.js → run/run-group.js} +9 -60
  177. package/dist/run/run-item-primitives.d.ts +5 -0
  178. package/dist/{run-ZsIKPA6J.js → run/run-item-primitives.js} +2 -2
  179. package/dist/run/run-row-shell.d.ts +53 -0
  180. package/dist/run/run-row-shell.js +114 -0
  181. package/dist/{tool-call-feed-BeB1_4pX.d.ts → run/tool-call-feed.d.ts} +7 -12
  182. package/dist/{tool-call-feed-BpmMLpPX.js → run/tool-call-feed.js} +4 -4
  183. package/dist/run/tool-call-step.d.ts +5 -0
  184. package/dist/run/tool-call-step.js +82 -0
  185. package/dist/run.d.ts +8 -2
  186. package/dist/run.js +7 -4
  187. package/dist/sdk-hooks.d.ts +8 -1
  188. package/dist/sdk-hooks.js +8 -2
  189. package/dist/stores/active-sessions-store.d.ts +83 -0
  190. package/dist/{active-sessions-store-Gt1R0rFG.js → stores/active-sessions-store.js} +1 -1
  191. package/dist/stores/chat-store.d.ts +14 -0
  192. package/dist/stores/chat-store.js +33 -0
  193. package/dist/stores.d.ts +3 -16
  194. package/dist/stores.js +2 -33
  195. package/dist/tool-previews/command-preview.d.ts +11 -0
  196. package/dist/tool-previews/command-preview.js +91 -0
  197. package/dist/tool-previews/diff-preview.d.ts +7 -0
  198. package/dist/tool-previews/diff-preview.js +76 -0
  199. package/dist/tool-previews/glob-results-preview.d.ts +7 -0
  200. package/dist/tool-previews/glob-results-preview.js +53 -0
  201. package/dist/tool-previews/grep-results-preview.d.ts +7 -0
  202. package/dist/tool-previews/grep-results-preview.js +99 -0
  203. package/dist/tool-previews/preview-primitives.js +58 -0
  204. package/dist/tool-previews/question-preview.d.ts +7 -0
  205. package/dist/tool-previews/question-preview.js +63 -0
  206. package/dist/tool-previews/web-search-preview.d.ts +7 -0
  207. package/dist/tool-previews/web-search-preview.js +78 -0
  208. package/dist/tool-previews/write-file-preview.d.ts +11 -0
  209. package/dist/tool-previews/write-file-preview.js +52 -0
  210. package/dist/tool-previews.d.ts +7 -1
  211. package/dist/tool-previews.js +7 -1
  212. package/dist/{branding-26d5ARIr.d.ts → types/branding.d.ts} +2 -3
  213. package/dist/{message-vhUI0c5G.d.ts → types/message.d.ts} +2 -3
  214. package/dist/{parts-5mhy9yJ3.d.ts → types/parts.d.ts} +8 -9
  215. package/dist/{run-OyWmIDqu.d.ts → types/run.d.ts} +9 -10
  216. package/dist/{tool-display-DgmL5rIU.d.ts → types/tool-display.d.ts} +5 -6
  217. package/dist/types.d.ts +5 -5
  218. package/dist/utils/copy-text.d.ts +7 -0
  219. package/dist/{utils-Bl_CZ0Qp.js → utils/copy-text.js} +1 -11
  220. package/dist/utils/format.d.ts +22 -0
  221. package/dist/{format-DfcLWqMa.js → utils/format.js} +1 -1
  222. package/dist/utils/time-ago.d.ts +3 -0
  223. package/dist/utils/time-ago.js +11 -0
  224. package/dist/utils/tool-display.d.ts +16 -0
  225. package/dist/{tool-display-BAuAVa43.js → utils/tool-display.js} +1 -1
  226. package/dist/utils.d.ts +6 -1
  227. package/dist/utils.js +6 -5
  228. package/package.json +2 -2
  229. package/src/files/rich-file-tree.stories.tsx +1 -1
  230. package/src/files/rich-file-tree.tsx +12 -9
  231. package/src/markdown/code-block.test.tsx +80 -2
  232. package/src/markdown/code-block.tsx +76 -80
  233. package/src/primitives/button.stories.tsx +37 -1
  234. package/src/primitives/button.test.tsx +230 -0
  235. package/src/primitives/button.tsx +62 -14
  236. package/src/primitives/card-anatomy.stories.tsx +51 -0
  237. package/src/primitives/card.test.tsx +71 -0
  238. package/src/primitives/card.tsx +25 -13
  239. package/src/primitives/heading.stories.tsx +35 -0
  240. package/src/primitives/heading.test.tsx +56 -0
  241. package/src/primitives/heading.tsx +76 -0
  242. package/src/primitives/index.ts +8 -0
  243. package/src/primitives/input.stories.tsx +28 -0
  244. package/src/primitives/input.test.tsx +161 -0
  245. package/src/primitives/input.tsx +34 -28
  246. package/src/primitives/page-header.stories.tsx +51 -0
  247. package/src/primitives/page-header.test.tsx +48 -0
  248. package/src/primitives/page-header.tsx +35 -35
  249. package/src/primitives/page-shell.stories.tsx +89 -0
  250. package/src/primitives/page-shell.test.tsx +24 -0
  251. package/src/primitives/page-shell.tsx +26 -0
  252. package/src/primitives/table-wrapper.stories.tsx +54 -0
  253. package/src/primitives/table.test.tsx +72 -0
  254. package/src/primitives/table.tsx +33 -12
  255. package/src/primitives/theme-toggle.test.tsx +167 -0
  256. package/src/primitives/theme-toggle.tsx +111 -39
  257. package/dist/active-sessions-store-nl70ZnMC.d.ts +0 -84
  258. package/dist/button-C87nEe2R.js +0 -78
  259. package/dist/chat-DDvF2oGQ.js +0 -672
  260. package/dist/diff-preview-BcK3nRXb.js +0 -533
  261. package/dist/document-editor-pane-BilJfiCo.d.ts +0 -136
  262. package/dist/document-editor-pane-POsq5Q0o.js +0 -1279
  263. package/dist/files-hywocVZz.js +0 -753
  264. package/dist/index-2UntyjBb.d.ts +0 -52
  265. package/dist/index-BgUHilZm.d.ts +0 -108
  266. package/dist/index-CJ8wRfiC.d.ts +0 -236
  267. package/dist/index-CPDET_s1.d.ts +0 -549
  268. package/dist/index-Cg6gYAqh.d.ts +0 -73
  269. package/dist/index-Cs7j8V7l.d.ts +0 -203
  270. package/dist/index-DjKxO357.d.ts +0 -128
  271. package/dist/primitives-Bx59fT-U.js +0 -1239
  272. package/dist/table-Bss73DhB.js +0 -146
  273. package/dist/tool-call-step-DNebqhmX.js +0 -434
  274. package/dist/use-auto-scroll-C5Pjeqon.js +0 -153
  275. package/dist/use-sse-stream-DjqRuDft.js +0 -884
  276. package/dist/use-sse-stream-Dm6R5YWz.d.ts +0 -282
@@ -0,0 +1,54 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { focusRing } from "../lib/focus";
3
+ import { Heading } from "./heading";
4
+ import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, type TableProps } from "./table";
5
+
6
+ const meta = {
7
+ title: "Primitives/Table/Wrapper",
8
+ component: Table,
9
+ parameters: { layout: "padded" },
10
+ } satisfies Meta<typeof Table>;
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ function WideTable(props: TableProps) {
15
+ return <Table className="min-w-[40rem]" {...props}>
16
+ <TableCaption>Build activity, including the full result identifier</TableCaption>
17
+ <TableHeader><TableRow><TableHead scope="col">Item</TableHead><TableHead scope="col">Result</TableHead><TableHead scope="col">Updated</TableHead></TableRow></TableHeader>
18
+ <TableBody><TableRow><TableHead scope="row">Documentation build</TableHead><TableCell>result_012345678901234567890123456789</TableCell><TableCell>Just now</TableCell></TableRow></TableBody>
19
+ </Table>;
20
+ }
21
+
22
+ const checkScrollOwner: NonNullable<Story["play"]> = ({ canvasElement }) => {
23
+ const regions = canvasElement.querySelectorAll<HTMLElement>('[role="region"]');
24
+ if (regions.length !== 1) throw new Error("Expected exactly one named scroll region");
25
+ const region = regions[0]!;
26
+ if (region.querySelector(".overflow-auto")) throw new Error("Table introduced a nested scroll container");
27
+ if (region.scrollWidth <= region.clientWidth) throw new Error("Wide-table fixture does not overflow");
28
+ region.focus();
29
+ if (region.ownerDocument.activeElement !== region) throw new Error("Scroll owner is not focusable");
30
+ region.scrollLeft = 24;
31
+ if (region.scrollLeft <= 0) throw new Error("Named scroll region cannot scroll");
32
+ region.scrollLeft = 0;
33
+ if (!region.querySelector("table > caption") || !region.querySelector('table > thead th[scope="col"]')) {
34
+ throw new Error("Native table semantics were lost");
35
+ }
36
+ };
37
+
38
+ export const AccessibleOverflow: Story = {
39
+ render: () => <div className="space-y-3" style={{ width: 320, maxWidth: "100%" }}>
40
+ <Heading variant="section">All activity columns</Heading>
41
+ <p className="text-sm text-muted-foreground">Tab to the named region and use the arrow keys to reveal the remaining columns.</p>
42
+ <WideTable wrapperProps={{ role: "region", "aria-label": "Activity columns", tabIndex: 0, className: focusRing }} />
43
+ </div>,
44
+ play: checkScrollOwner,
45
+ };
46
+ export const CallerOwnedScroll: Story = {
47
+ render: () => <div className="space-y-3" style={{ width: 320, maxWidth: "100%" }}>
48
+ <Heading variant="section">Caller-owned scrolling</Heading>
49
+ <div role="region" aria-label="Activity columns" tabIndex={0} className={`overflow-auto ${focusRing}`}>
50
+ <WideTable wrapper={false} />
51
+ </div>
52
+ </div>,
53
+ play: checkScrollOwner,
54
+ };
@@ -0,0 +1,72 @@
1
+ import { createRef } from "react";
2
+ import { render, screen, within } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, type TableProps } from "./table";
6
+
7
+ function UsageTable(props: TableProps) {
8
+ return <Table {...props}>
9
+ <TableCaption>Usage</TableCaption>
10
+ <TableHeader><TableRow><TableHead scope="col" id="resource">Resource</TableHead><TableHead scope="col" id="count">Count</TableHead></TableRow></TableHeader>
11
+ <TableBody><TableRow><TableHead scope="row" id="builds">Builds</TableHead><TableCell headers="builds count">3</TableCell></TableRow></TableBody>
12
+ <TableFooter><TableRow><TableCell colSpan={2}>One resource</TableCell></TableRow></TableFooter>
13
+ </Table>;
14
+ }
15
+
16
+ describe("Table", () => {
17
+ it("preserves the default wrapper without imposing an unnamed region or tab stop", () => {
18
+ render(<UsageTable />);
19
+ const table = screen.getByRole("table", { name: "Usage" });
20
+ expect(table.parentElement?.tagName).toBe("DIV");
21
+ expect(table.parentElement).toHaveClass("relative", "w-full", "overflow-auto");
22
+ expect(table.parentElement).not.toHaveAttribute("role");
23
+ expect(table.parentElement).not.toHaveAttribute("tabindex");
24
+ });
25
+
26
+ it("retains native caption, table sections, headers, scope and spans", () => {
27
+ render(<UsageTable />);
28
+ const table = screen.getByRole("table", { name: "Usage" });
29
+ expect(Array.from(table.children).map((node) => node.tagName)).toEqual(["CAPTION", "THEAD", "TBODY", "TFOOT"]);
30
+ expect(screen.getByRole("columnheader", { name: "Resource" })).toHaveAttribute("scope", "col");
31
+ expect(screen.getByRole("rowheader", { name: "Builds" })).toHaveAttribute("scope", "row");
32
+ expect(screen.getByRole("cell", { name: "3" })).toHaveAttribute("headers", "builds count");
33
+ expect(screen.getByRole("cell", { name: "One resource" })).toHaveAttribute("colspan", "2");
34
+ expect(screen.queryByRole("grid")).toBeNull();
35
+ });
36
+
37
+ it("allows a caller-named keyboard-focusable wrapper separately from table attributes", async () => {
38
+ const user = userEvent.setup();
39
+ render(<><h2 id="usage-title">Resource usage</h2><UsageTable id="usage-table" className="min-w-[40rem]" wrapperProps={{ role: "region", "aria-labelledby": "usage-title", tabIndex: 0, className: "max-h-80" }} /></>);
40
+ const region = screen.getByRole("region", { name: "Resource usage" });
41
+ const table = within(region).getByRole("table", { name: "Usage" });
42
+ expect(region).toHaveClass("max-h-80", "overflow-auto");
43
+ expect(table).toHaveAttribute("id", "usage-table");
44
+ expect(table).not.toHaveAttribute("tabindex");
45
+ expect(table).not.toHaveClass("max-h-80");
46
+ await user.tab();
47
+ expect(region).toHaveFocus();
48
+ });
49
+
50
+ it("returns a bare table for a caller-owned scroll region, without leaking wrapper props", () => {
51
+ render(<div role="region" aria-label="Outer" className="overflow-auto">
52
+ <UsageTable wrapper={false} wrapperProps={{ id: "unused-wrapper" }} />
53
+ </div>);
54
+ const outer = screen.getByRole("region", { name: "Outer" });
55
+ const table = screen.getByRole("table", { name: "Usage" });
56
+ expect(outer.firstElementChild).toBe(table);
57
+ expect(outer.querySelectorAll(".overflow-auto")).toHaveLength(0);
58
+ expect(table).not.toHaveAttribute("wrapper");
59
+ expect(table).not.toHaveAttribute("wrapperProps");
60
+ expect(table).not.toHaveAttribute("id", "unused-wrapper");
61
+ });
62
+
63
+ it("keeps the public ref and native attributes/handlers on the table", async () => {
64
+ const ref = createRef<HTMLTableElement>();
65
+ const onClick = vi.fn();
66
+ render(<Table ref={ref} cellPadding={0} onClick={onClick} aria-label="Details"><TableBody><TableRow><TableCell>Open</TableCell></TableRow></TableBody></Table>);
67
+ expect(ref.current).toBe(screen.getByRole("table", { name: "Details" }));
68
+ expect(ref.current).toHaveAttribute("cellpadding", "0");
69
+ await userEvent.setup().click(screen.getByRole("cell", { name: "Open" }));
70
+ expect(onClick).toHaveBeenCalledTimes(1);
71
+ });
72
+ });
@@ -1,18 +1,39 @@
1
1
  import * as React from "react";
2
2
  import { cn } from "../lib/utils";
3
3
 
4
- const Table = React.forwardRef<
5
- HTMLTableElement,
6
- React.HTMLAttributes<HTMLTableElement>
7
- >(({ className, ...props }, ref) => (
8
- <div className="relative w-full overflow-auto">
9
- <table
10
- ref={ref}
11
- className={cn("w-full caption-bottom text-sm", className)}
12
- {...props}
13
- />
14
- </div>
15
- ));
4
+ export interface TableProps extends React.TableHTMLAttributes<HTMLTableElement> {
5
+ /** Keep the existing scroll wrapper by default; false returns only the native table. */
6
+ wrapper?: boolean;
7
+ /**
8
+ * Attributes for the wrapper, not the table (ignored when wrapper is false).
9
+ * For an overflowing table, supply tabIndex and an accessible region name.
10
+ * No extra tab stop or unnamed landmark is imposed on every small table.
11
+ */
12
+ wrapperProps?: Omit<React.HTMLAttributes<HTMLDivElement>, "children" | "dangerouslySetInnerHTML">;
13
+ }
14
+
15
+ const Table = React.forwardRef<HTMLTableElement, TableProps>(
16
+ ({ className, wrapper = true, wrapperProps, ...props }, ref) => {
17
+ const table = (
18
+ <table
19
+ ref={ref}
20
+ className={cn("w-full caption-bottom text-sm", className)}
21
+ {...props}
22
+ />
23
+ );
24
+ if (!wrapper) return table;
25
+
26
+ const { className: wrapperClassName, ...attributes } = wrapperProps ?? {};
27
+ return (
28
+ <div
29
+ {...attributes}
30
+ className={cn("relative w-full overflow-auto", wrapperClassName)}
31
+ >
32
+ {table}
33
+ </div>
34
+ );
35
+ },
36
+ );
16
37
  Table.displayName = "Table";
17
38
 
18
39
  const TableHeader = React.forwardRef<
@@ -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 { useCallback, useEffect, useState } from "react";
1
+ import { useEffect, useSyncExternalStore } from "react";
2
2
 
3
3
  type Theme = "light" | "dark" | "system";
4
+ type Mode = Exclude<Theme, "system">;
4
5
 
5
- function getSystemTheme(): "light" | "dark" {
6
- return window.matchMedia("(prefers-color-scheme: dark)").matches
7
- ? "dark"
8
- : "light";
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 applyTheme(theme: Theme) {
12
- const resolved = theme === "system" ? getSystemTheme() : theme;
13
- document.documentElement.classList.toggle("dark", resolved === "dark");
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
- export function useTheme() {
17
- const [theme, setThemeState] = useState<Theme>(() => {
18
- if (typeof window === "undefined") return "system";
19
- return (localStorage.getItem("theme") as Theme) ?? "system";
20
- });
21
-
22
- const setTheme = useCallback((next: Theme) => {
23
- setThemeState(next);
24
- if (next === "system") {
25
- localStorage.removeItem("theme");
26
- } else {
27
- localStorage.setItem("theme", next);
28
- }
29
- applyTheme(next);
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
- applyTheme(theme);
34
- if (theme !== "system") return;
35
- const mq = window.matchMedia("(prefers-color-scheme: dark)");
36
- const handler = () => applyTheme("system");
37
- mq.addEventListener("change", handler);
38
- return () => mq.removeEventListener("change", handler);
39
- }, [theme]);
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 { theme, setTheme } = useTheme();
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(resolved === "dark" ? "light" : "dark")}
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 ${resolved === "dark" ? "light" : "dark"} mode`}
163
+ aria-label={`Switch to ${resolvedTheme === "dark" ? "light" : "dark"} mode`}
92
164
  >
93
- {resolved === "dark" ? <SunIcon /> : <MoonIcon />}
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 };