hermium 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +56 -0
  2. package/bin/hermium.mjs +185 -164
  3. package/dist/api.mjs +3513 -0
  4. package/dist/public/assets/css/index-Dfs9RUU9.css +1 -0
  5. package/dist/public/assets/css/styles-B8p6jk5Z.css +1 -0
  6. package/dist/public/assets/js/ChatInputBlock-Bw7AL70H.js +1 -0
  7. package/dist/public/assets/js/MarkdownMessage-8d7Y6VL-.js +1 -0
  8. package/dist/public/assets/js/base-ui-BvQbAt_1.js +1 -0
  9. package/dist/public/assets/js/chat._sessionId-BG6lVraH.js +1 -0
  10. package/dist/public/assets/js/chat.index-D2zdMPTT.js +1 -0
  11. package/dist/public/assets/js/index-C0AK45FU.js +60 -0
  12. package/dist/public/assets/js/index-Cx5En4FK.js +1 -0
  13. package/dist/public/assets/js/memory-CeSRdTkW.js +3 -0
  14. package/dist/public/assets/js/router-8uDKazL-.js +1 -0
  15. package/dist/public/assets/js/settings-Bc3Y5zXO.js +1 -0
  16. package/dist/public/assets/js/skills-DZv7sA_5.js +1 -0
  17. package/dist/public/assets/js/theme-CPkdkpaj.js +1 -0
  18. package/dist/public/assets/js/usage-DXQsT9_b.js +1 -0
  19. package/dist/public/assets/woff2/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  20. package/dist/public/assets/woff2/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  21. package/dist/public/assets/woff2/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  22. package/dist/public/assets/woff2/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  23. package/dist/public/assets/woff2/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  24. package/dist/public/favicon.ico +0 -0
  25. package/dist/public/logo.png +0 -0
  26. package/package.json +1 -1
  27. package/dist/public/assets/IconAlertCircle-BHkmI3j7.js +0 -1
  28. package/dist/public/assets/IconAlertTriangle-wCJudlVg.js +0 -1
  29. package/dist/public/assets/IconCheck-CFuEh_p7.js +0 -1
  30. package/dist/public/assets/IconLoader2-BIx3OuF9.js +0 -1
  31. package/dist/public/assets/IconRefresh-Dgm93w3T.js +0 -1
  32. package/dist/public/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  33. package/dist/public/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  34. package/dist/public/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  35. package/dist/public/assets/index-Bbz3abmO.js +0 -14
  36. package/dist/public/assets/index-CWUaRwcE.js +0 -1
  37. package/dist/public/assets/index-CinLq3cd.js +0 -1
  38. package/dist/public/assets/index-CrQs9n6q.js +0 -29
  39. package/dist/public/assets/index-CtacpN3I.js +0 -1
  40. package/dist/public/assets/index-DY7aE-9s.js +0 -2
  41. package/dist/public/assets/index-DkYGodJj.js +0 -94
  42. package/dist/public/assets/index-DvDLadUx.js +0 -1
  43. package/dist/public/assets/index-U6RcWedt.js +0 -1
  44. package/dist/public/assets/index-_6iFZ0fh.js +0 -1
  45. package/dist/public/assets/index-enFS26SU.js +0 -1
  46. package/dist/public/assets/input-eNcwlDHp.js +0 -1
  47. package/dist/public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  48. package/dist/public/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  49. package/dist/public/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  50. package/dist/public/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  51. package/dist/public/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  52. package/dist/public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  53. package/dist/public/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  54. package/dist/public/assets/queries-iHRgZzw2.js +0 -1
  55. package/dist/public/assets/styles-KcflDlA_.css +0 -1
  56. package/dist/public/assets/switch-B1DcZLwL.js +0 -1
  57. package/dist/public/assets/syntax-highlighter-DWPF-A_h.js +0 -6
  58. package/dist/public/assets/textarea-Di_syYTS.js +0 -1
  59. package/dist/public/favicon.png +0 -0
  60. package/dist/public/nous-logo.png +0 -0
  61. package/dist/server/index.mjs +0 -244
  62. package/dist/web-server/__23tanstack-start-plugin-adapters-Cwee5PKy.mjs +0 -6
  63. package/dist/web-server/_chunks/ssr-renderer.mjs +0 -22
  64. package/dist/web-server/_libs/babel__runtime.mjs +0 -237
  65. package/dist/web-server/_libs/bail.mjs +0 -8
  66. package/dist/web-server/_libs/base-ui__react.mjs +0 -9554
  67. package/dist/web-server/_libs/base-ui__utils.mjs +0 -1101
  68. package/dist/web-server/_libs/ccount.mjs +0 -16
  69. package/dist/web-server/_libs/character-entities-legacy.mjs +0 -111
  70. package/dist/web-server/_libs/character-entities.mjs +0 -2130
  71. package/dist/web-server/_libs/character-reference-invalid.mjs +0 -33
  72. package/dist/web-server/_libs/class-variance-authority.mjs +0 -44
  73. package/dist/web-server/_libs/clsx.mjs +0 -16
  74. package/dist/web-server/_libs/comma-separated-tokens.mjs +0 -31
  75. package/dist/web-server/_libs/cookie-es.mjs +0 -44
  76. package/dist/web-server/_libs/croner.mjs +0 -1
  77. package/dist/web-server/_libs/crossws.mjs +0 -1
  78. package/dist/web-server/_libs/decode-named-character-reference+[...].mjs +0 -8
  79. package/dist/web-server/_libs/devlop.mjs +0 -8
  80. package/dist/web-server/_libs/escape-string-regexp.mjs +0 -9
  81. package/dist/web-server/_libs/estree-util-is-identifier-name.mjs +0 -11
  82. package/dist/web-server/_libs/extend.mjs +0 -97
  83. package/dist/web-server/_libs/fault.mjs +0 -1
  84. package/dist/web-server/_libs/floating-ui__core.mjs +0 -663
  85. package/dist/web-server/_libs/floating-ui__dom.mjs +0 -624
  86. package/dist/web-server/_libs/floating-ui__react-dom.mjs +0 -279
  87. package/dist/web-server/_libs/floating-ui__utils.mjs +0 -322
  88. package/dist/web-server/_libs/format.mjs +0 -1
  89. package/dist/web-server/_libs/h3.mjs +0 -408
  90. package/dist/web-server/_libs/hast-util-parse-selector.mjs +0 -39
  91. package/dist/web-server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
  92. package/dist/web-server/_libs/hast-util-whitespace.mjs +0 -10
  93. package/dist/web-server/_libs/hastscript.mjs +0 -200
  94. package/dist/web-server/_libs/highlight.js.mjs +0 -1
  95. package/dist/web-server/_libs/hookable.mjs +0 -1
  96. package/dist/web-server/_libs/html-url-attributes.mjs +0 -26
  97. package/dist/web-server/_libs/inline-style-parser.mjs +0 -142
  98. package/dist/web-server/_libs/is-alphabetical.mjs +0 -7
  99. package/dist/web-server/_libs/is-alphanumerical.mjs +0 -8
  100. package/dist/web-server/_libs/is-decimal.mjs +0 -7
  101. package/dist/web-server/_libs/is-hexadecimal.mjs +0 -7
  102. package/dist/web-server/_libs/is-plain-obj.mjs +0 -10
  103. package/dist/web-server/_libs/isbot.mjs +0 -21
  104. package/dist/web-server/_libs/longest-streak.mjs +0 -25
  105. package/dist/web-server/_libs/lowlight.mjs +0 -1
  106. package/dist/web-server/_libs/markdown-table.mjs +0 -142
  107. package/dist/web-server/_libs/mdast-util-find-and-replace.mjs +0 -109
  108. package/dist/web-server/_libs/mdast-util-from-markdown.mjs +0 -717
  109. package/dist/web-server/_libs/mdast-util-gfm-autolink-literal+[...].mjs +0 -156
  110. package/dist/web-server/_libs/mdast-util-gfm-footnote.mjs +0 -117
  111. package/dist/web-server/_libs/mdast-util-gfm-strikethrough.mjs +0 -54
  112. package/dist/web-server/_libs/mdast-util-gfm-table.mjs +0 -157
  113. package/dist/web-server/_libs/mdast-util-gfm-task-list-item.mjs +0 -77
  114. package/dist/web-server/_libs/mdast-util-gfm.mjs +0 -29
  115. package/dist/web-server/_libs/mdast-util-phrasing.mjs +0 -30
  116. package/dist/web-server/_libs/mdast-util-to-hast.mjs +0 -710
  117. package/dist/web-server/_libs/mdast-util-to-markdown.mjs +0 -798
  118. package/dist/web-server/_libs/mdast-util-to-string.mjs +0 -38
  119. package/dist/web-server/_libs/micromark-core-commonmark.mjs +0 -2259
  120. package/dist/web-server/_libs/micromark-extension-gfm-autolink-literal+[...].mjs +0 -344
  121. package/dist/web-server/_libs/micromark-extension-gfm-footnote+[...].mjs +0 -279
  122. package/dist/web-server/_libs/micromark-extension-gfm-strikethrough+[...].mjs +0 -98
  123. package/dist/web-server/_libs/micromark-extension-gfm-table.mjs +0 -491
  124. package/dist/web-server/_libs/micromark-extension-gfm-tagfilter+[...].mjs +0 -1
  125. package/dist/web-server/_libs/micromark-extension-gfm-task-list-item+[...].mjs +0 -77
  126. package/dist/web-server/_libs/micromark-extension-gfm.mjs +0 -18
  127. package/dist/web-server/_libs/micromark-factory-destination.mjs +0 -94
  128. package/dist/web-server/_libs/micromark-factory-label.mjs +0 -63
  129. package/dist/web-server/_libs/micromark-factory-space.mjs +0 -24
  130. package/dist/web-server/_libs/micromark-factory-title.mjs +0 -65
  131. package/dist/web-server/_libs/micromark-factory-whitespace.mjs +0 -22
  132. package/dist/web-server/_libs/micromark-util-character.mjs +0 -44
  133. package/dist/web-server/_libs/micromark-util-chunked.mjs +0 -36
  134. package/dist/web-server/_libs/micromark-util-classify-character+[...].mjs +0 -12
  135. package/dist/web-server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
  136. package/dist/web-server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
  137. package/dist/web-server/_libs/micromark-util-decode-string.mjs +0 -21
  138. package/dist/web-server/_libs/micromark-util-encode.mjs +0 -1
  139. package/dist/web-server/_libs/micromark-util-html-tag-name.mjs +0 -69
  140. package/dist/web-server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
  141. package/dist/web-server/_libs/micromark-util-resolve-all.mjs +0 -15
  142. package/dist/web-server/_libs/micromark-util-sanitize-uri.mjs +0 -41
  143. package/dist/web-server/_libs/micromark-util-subtokenize.mjs +0 -346
  144. package/dist/web-server/_libs/micromark.mjs +0 -906
  145. package/dist/web-server/_libs/ocache.mjs +0 -1
  146. package/dist/web-server/_libs/ohash.mjs +0 -1
  147. package/dist/web-server/_libs/parse-entities.mjs +0 -245
  148. package/dist/web-server/_libs/property-information.mjs +0 -1210
  149. package/dist/web-server/_libs/react-dom.mjs +0 -10779
  150. package/dist/web-server/_libs/react-markdown.mjs +0 -147
  151. package/dist/web-server/_libs/react-syntax-highlighter.mjs +0 -941
  152. package/dist/web-server/_libs/react.mjs +0 -513
  153. package/dist/web-server/_libs/refractor.mjs +0 -2425
  154. package/dist/web-server/_libs/remark-gfm.mjs +0 -20
  155. package/dist/web-server/_libs/remark-parse.mjs +0 -19
  156. package/dist/web-server/_libs/remark-rehype.mjs +0 -21
  157. package/dist/web-server/_libs/reselect.mjs +0 -1
  158. package/dist/web-server/_libs/rou3.mjs +0 -8
  159. package/dist/web-server/_libs/seroval-plugins.mjs +0 -58
  160. package/dist/web-server/_libs/seroval.mjs +0 -1775
  161. package/dist/web-server/_libs/space-separated-tokens.mjs +0 -11
  162. package/dist/web-server/_libs/srvx.mjs +0 -781
  163. package/dist/web-server/_libs/style-to-js.mjs +0 -72
  164. package/dist/web-server/_libs/style-to-object.mjs +0 -38
  165. package/dist/web-server/_libs/tabler__icons-react.mjs +0 -230
  166. package/dist/web-server/_libs/tanstack__history.mjs +0 -204
  167. package/dist/web-server/_libs/tanstack__query-core.mjs +0 -2552
  168. package/dist/web-server/_libs/tanstack__react-query.mjs +0 -190
  169. package/dist/web-server/_libs/tanstack__react-router.mjs +0 -1120
  170. package/dist/web-server/_libs/tanstack__react-store.mjs +0 -2
  171. package/dist/web-server/_libs/tanstack__router-core.mjs +0 -4288
  172. package/dist/web-server/_libs/tanstack__store.mjs +0 -1
  173. package/dist/web-server/_libs/trim-lines.mjs +0 -41
  174. package/dist/web-server/_libs/trough.mjs +0 -85
  175. package/dist/web-server/_libs/ufo.mjs +0 -54
  176. package/dist/web-server/_libs/unctx.mjs +0 -1
  177. package/dist/web-server/_libs/ungap__structured-clone.mjs +0 -224
  178. package/dist/web-server/_libs/unified.mjs +0 -661
  179. package/dist/web-server/_libs/unist-util-is.mjs +0 -100
  180. package/dist/web-server/_libs/unist-util-position.mjs +0 -27
  181. package/dist/web-server/_libs/unist-util-stringify-position.mjs +0 -27
  182. package/dist/web-server/_libs/unist-util-visit-parents.mjs +0 -83
  183. package/dist/web-server/_libs/unist-util-visit.mjs +0 -24
  184. package/dist/web-server/_libs/unstorage.mjs +0 -1
  185. package/dist/web-server/_libs/use-sync-external-store.mjs +0 -139
  186. package/dist/web-server/_libs/vfile-message.mjs +0 -138
  187. package/dist/web-server/_libs/vfile.mjs +0 -467
  188. package/dist/web-server/_libs/zod.mjs +0 -3915
  189. package/dist/web-server/_libs/zustand.mjs +0 -343
  190. package/dist/web-server/_libs/zwitch.mjs +0 -1
  191. package/dist/web-server/_ssr/index-0n2Z3BPQ.mjs +0 -369
  192. package/dist/web-server/_ssr/index-6itDALOw.mjs +0 -339
  193. package/dist/web-server/_ssr/index-BIRTrOmp.mjs +0 -449
  194. package/dist/web-server/_ssr/index-BPzfADac.mjs +0 -66
  195. package/dist/web-server/_ssr/index-BQE3bF14.mjs +0 -1870
  196. package/dist/web-server/_ssr/index-C5HpvlUP.mjs +0 -190
  197. package/dist/web-server/_ssr/index-C_ZxnypN.mjs +0 -213
  198. package/dist/web-server/_ssr/index-Ca8JFH8f.mjs +0 -612
  199. package/dist/web-server/_ssr/index-DNVESZiA.mjs +0 -513
  200. package/dist/web-server/_ssr/index.mjs +0 -1558
  201. package/dist/web-server/_ssr/input-CqXjTRQg.mjs +0 -20
  202. package/dist/web-server/_ssr/queries-3H_19mUt.mjs +0 -16
  203. package/dist/web-server/_ssr/router-sbsNus0Y.mjs +0 -2093
  204. package/dist/web-server/_ssr/start-HYkvq4Ni.mjs +0 -4
  205. package/dist/web-server/_ssr/switch-usf2F1UM.mjs +0 -33
  206. package/dist/web-server/_ssr/syntax-highlighter-5vezNTce.mjs +0 -62
  207. package/dist/web-server/_ssr/textarea-DfRheWY0.mjs +0 -18
  208. package/dist/web-server/_tanstack-start-manifest_v-DqW-pKEH.mjs +0 -4
  209. package/dist/web-server/index.mjs +0 -597
@@ -1,2093 +0,0 @@
1
- import { c as createRouter, a as createRootRoute, b as createFileRoute, l as lazyRouteComponent, H as HeadContent, S as Scripts, u as useLocation, L as Link } from "../_libs/tanstack__react-router.mjs";
2
- import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
3
- import { b as QueryClient } from "../_libs/tanstack__query-core.mjs";
4
- import { Q as QueryClientProvider, u as useQueryClient, a as useQuery, b as useMutation } from "../_libs/tanstack__react-query.mjs";
5
- import { c as clsx } from "../_libs/clsx.mjs";
6
- import { c as cva } from "../_libs/class-variance-authority.mjs";
7
- import { c as create, d as devtools } from "../_libs/zustand.mjs";
8
- import { I as IconSearch, a as IconX, b as IconSettings, c as IconSun, d as IconMoon, e as IconPaint, f as IconBrandGithub, g as IconArrowLeft, h as IconLayoutSidebar, i as IconChevronUp, j as IconChevronDown, k as IconCirclePlus, l as IconBrain, m as IconPuzzle, n as IconRobot, o as IconHash, p as IconChartBar, q as IconPin, r as IconDots, s as IconPencil, t as IconPinnedOff, u as IconCopy, v as IconTrash, w as IconDownload } from "../_libs/tabler__icons-react.mjs";
9
- import { T as TooltipProvider$1, u as useRender, m as mergeProps, B as Button$1, D as DialogRoot, a as DialogPopup, b as DialogClose, c as DialogTitle, d as DialogDescription, C as CollapsibleRoot, e as CollapsibleTrigger$1, f as CollapsiblePanel, M as MenuRoot, g as MenuTrigger, h as MenuPortal, i as MenuPositioner, j as MenuPopup, k as MenuItem, l as DialogPortal, n as DialogBackdrop, o as TooltipRoot, p as TooltipPortal, q as TooltipPositioner, r as TooltipPopup, s as TooltipArrow, t as TooltipTrigger$1 } from "../_libs/base-ui__react.mjs";
10
- import { o as object, s as string } from "../_libs/zod.mjs";
11
- import "../_libs/tanstack__router-core.mjs";
12
- import "../_libs/tanstack__history.mjs";
13
- import "../_libs/cookie-es.mjs";
14
- import "../_libs/seroval.mjs";
15
- import "../_libs/seroval-plugins.mjs";
16
- import "node:stream/web";
17
- import "node:stream";
18
- import "../_libs/react-dom.mjs";
19
- import "util";
20
- import "crypto";
21
- import "async_hooks";
22
- import "stream";
23
- import "../_libs/isbot.mjs";
24
- import "../_libs/base-ui__utils.mjs";
25
- import "../_libs/use-sync-external-store.mjs";
26
- import "../_libs/floating-ui__utils.mjs";
27
- import "../_libs/floating-ui__react-dom.mjs";
28
- import "../_libs/floating-ui__dom.mjs";
29
- import "../_libs/floating-ui__core.mjs";
30
- const queryClient = new QueryClient({
31
- defaultOptions: {
32
- queries: {
33
- staleTime: 3e4,
34
- // 30s before re-fetch
35
- gcTime: 5 * 6e4,
36
- // 5min garbage collection
37
- retry: 1,
38
- refetchOnWindowFocus: false
39
- },
40
- mutations: {
41
- retry: 0
42
- }
43
- }
44
- });
45
- const CONFLICT_PREFIXES = /* @__PURE__ */ new Set([
46
- "p-",
47
- "px-",
48
- "py-",
49
- "pt-",
50
- "pr-",
51
- "pb-",
52
- "pl-",
53
- "m-",
54
- "mx-",
55
- "my-",
56
- "mt-",
57
- "mr-",
58
- "mb-",
59
- "ml-",
60
- "w-",
61
- "h-",
62
- "min-w-",
63
- "min-h-",
64
- "max-w-",
65
- "max-h-",
66
- "gap-",
67
- "gap-x-",
68
- "gap-y-",
69
- "inset-",
70
- "inset-x-",
71
- "inset-y-",
72
- "top-",
73
- "right-",
74
- "bottom-",
75
- "left-",
76
- "z-",
77
- "bg-",
78
- "text-",
79
- "border-",
80
- "ring-",
81
- "shadow-",
82
- "rounded-",
83
- "rounded-t-",
84
- "rounded-r-",
85
- "rounded-b-",
86
- "rounded-l-",
87
- "rounded-tl-",
88
- "rounded-tr-",
89
- "rounded-br-",
90
- "rounded-bl-",
91
- "font-",
92
- "leading-",
93
- "tracking-",
94
- "opacity-",
95
- "scale-",
96
- "rotate-",
97
- "translate-x-",
98
- "translate-y-",
99
- "skew-x-",
100
- "skew-y-",
101
- "divide-x-",
102
- "divide-y-",
103
- "space-x-",
104
- "space-y-",
105
- "flex-",
106
- "grid-cols-",
107
- "grid-rows-",
108
- "col-",
109
- "col-start-",
110
- "col-end-",
111
- "row-",
112
- "row-start-",
113
- "row-end-",
114
- "order-",
115
- "duration-",
116
- "delay-",
117
- "ease-",
118
- "size-",
119
- "blur-",
120
- "brightness-",
121
- "contrast-",
122
- "grayscale-"
123
- ]);
124
- function getPrefix(cls) {
125
- for (let i = cls.length; i > 0; i--) {
126
- const prefix = cls.slice(0, i);
127
- if (CONFLICT_PREFIXES.has(prefix)) return prefix;
128
- }
129
- return null;
130
- }
131
- function cn(...inputs) {
132
- const classes = clsx(inputs);
133
- if (!classes) return "";
134
- const seen = /* @__PURE__ */ new Map();
135
- const result = [];
136
- for (const cls of classes.split(" ")) {
137
- if (!cls) continue;
138
- const prefix = getPrefix(cls);
139
- if (prefix) {
140
- if (seen.has(prefix)) {
141
- const prevIdx = result.indexOf(seen.get(prefix));
142
- if (prevIdx >= 0) {
143
- result[prevIdx] = cls;
144
- }
145
- } else {
146
- seen.set(prefix, cls);
147
- result.push(cls);
148
- }
149
- } else {
150
- result.push(cls);
151
- }
152
- }
153
- return result.join(" ");
154
- }
155
- function TooltipProvider({
156
- delay = 0,
157
- ...props
158
- }) {
159
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
160
- TooltipProvider$1,
161
- {
162
- "data-slot": "tooltip-provider",
163
- delay,
164
- ...props
165
- }
166
- );
167
- }
168
- function Tooltip({ ...props }) {
169
- return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipRoot, { "data-slot": "tooltip", ...props });
170
- }
171
- function TooltipTrigger({ ...props }) {
172
- return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger$1, { "data-slot": "tooltip-trigger", ...props });
173
- }
174
- function TooltipContent({
175
- className,
176
- side = "top",
177
- sideOffset = 4,
178
- align = "center",
179
- alignOffset = 0,
180
- children,
181
- ...props
182
- }) {
183
- return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
184
- TooltipPositioner,
185
- {
186
- align,
187
- alignOffset,
188
- side,
189
- sideOffset,
190
- className: "isolate z-50",
191
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
192
- TooltipPopup,
193
- {
194
- "data-slot": "tooltip-content",
195
- className: cn(
196
- "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
197
- className
198
- ),
199
- ...props,
200
- children: [
201
- children,
202
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipArrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" })
203
- ]
204
- }
205
- )
206
- }
207
- ) });
208
- }
209
- const THEME_KEY = "hermium-theme";
210
- function getStoredTheme() {
211
- try {
212
- const stored = localStorage.getItem(THEME_KEY);
213
- if (stored === "light" || stored === "dark" || stored === "system") return stored;
214
- } catch {
215
- }
216
- return "system";
217
- }
218
- function setStoredTheme(theme) {
219
- try {
220
- localStorage.setItem(THEME_KEY, theme);
221
- } catch {
222
- }
223
- }
224
- function getSystemTheme() {
225
- if (typeof window === "undefined") return "light";
226
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
227
- }
228
- function applyTheme(theme) {
229
- const root = document.documentElement;
230
- const resolved = theme === "system" ? getSystemTheme() : theme;
231
- if (resolved === "dark") {
232
- root.classList.add("dark");
233
- } else {
234
- root.classList.remove("dark");
235
- }
236
- }
237
- const ThemeContext = reactExports.createContext({
238
- theme: "system",
239
- resolvedTheme: "light",
240
- setTheme: () => {
241
- }
242
- });
243
- function ThemeProvider({ children }) {
244
- const [theme, setThemeState] = reactExports.useState("system");
245
- const [resolved, setResolved] = reactExports.useState("light");
246
- const [mounted, setMounted] = reactExports.useState(false);
247
- reactExports.useEffect(() => {
248
- setMounted(true);
249
- const stored = getStoredTheme();
250
- setThemeState(stored);
251
- applyTheme(stored);
252
- const isDark = stored === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches : stored === "dark";
253
- setResolved(isDark ? "dark" : "light");
254
- const mq = window.matchMedia("(prefers-color-scheme: dark)");
255
- const handler = (e) => {
256
- const t = getStoredTheme();
257
- if (t === "system") {
258
- applyTheme("system");
259
- setResolved(e.matches ? "dark" : "light");
260
- }
261
- };
262
- mq.addEventListener("change", handler);
263
- return () => mq.removeEventListener("change", handler);
264
- }, []);
265
- const setTheme = reactExports.useCallback((t) => {
266
- setThemeState(t);
267
- setStoredTheme(t);
268
- applyTheme(t);
269
- const isDark = t === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches : t === "dark";
270
- setResolved(isDark ? "dark" : "light");
271
- }, []);
272
- if (!mounted) {
273
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
274
- ThemeContext.Provider,
275
- {
276
- value: { theme: "system", resolvedTheme: "light", setTheme: () => {
277
- } },
278
- children
279
- }
280
- );
281
- }
282
- return /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeContext.Provider, { value: { theme, resolvedTheme: resolved, setTheme }, children });
283
- }
284
- function useTheme() {
285
- return reactExports.useContext(ThemeContext);
286
- }
287
- const MOBILE_BREAKPOINT = 768;
288
- function useIsMobile() {
289
- const [isMobile, setIsMobile] = reactExports.useState(void 0);
290
- reactExports.useEffect(() => {
291
- const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
292
- const onChange = () => {
293
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
294
- };
295
- mql.addEventListener("change", onChange);
296
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
297
- return () => mql.removeEventListener("change", onChange);
298
- }, []);
299
- return !!isMobile;
300
- }
301
- const buttonVariants = cva(
302
- "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
303
- {
304
- variants: {
305
- variant: {
306
- default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
307
- outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
308
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
309
- ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
310
- destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
311
- link: "text-primary underline-offset-4 hover:underline"
312
- },
313
- size: {
314
- default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
315
- xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
316
- sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
317
- lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
318
- icon: "size-8",
319
- "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
320
- "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
321
- "icon-lg": "size-9"
322
- }
323
- },
324
- defaultVariants: {
325
- variant: "default",
326
- size: "default"
327
- }
328
- }
329
- );
330
- function Button({
331
- className,
332
- variant = "default",
333
- size = "default",
334
- ...props
335
- }) {
336
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
337
- Button$1,
338
- {
339
- "data-slot": "button",
340
- className: cn(buttonVariants({ variant, size, className })),
341
- ...props
342
- }
343
- );
344
- }
345
- function Sheet({ ...props }) {
346
- return /* @__PURE__ */ jsxRuntimeExports.jsx(DialogRoot, { "data-slot": "sheet", ...props });
347
- }
348
- function SheetPortal({ ...props }) {
349
- return /* @__PURE__ */ jsxRuntimeExports.jsx(DialogPortal, { "data-slot": "sheet-portal", ...props });
350
- }
351
- function SheetOverlay({ className, ...props }) {
352
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
353
- DialogBackdrop,
354
- {
355
- "data-slot": "sheet-overlay",
356
- className: cn(
357
- "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
358
- className
359
- ),
360
- ...props
361
- }
362
- );
363
- }
364
- function SheetContent({
365
- className,
366
- children,
367
- side = "right",
368
- showCloseButton = true,
369
- ...props
370
- }) {
371
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(SheetPortal, { children: [
372
- /* @__PURE__ */ jsxRuntimeExports.jsx(SheetOverlay, {}),
373
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
374
- DialogPopup,
375
- {
376
- "data-slot": "sheet-content",
377
- "data-side": side,
378
- className: cn(
379
- "fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
380
- className
381
- ),
382
- ...props,
383
- children: [
384
- children,
385
- showCloseButton && /* @__PURE__ */ jsxRuntimeExports.jsxs(
386
- DialogClose,
387
- {
388
- "data-slot": "sheet-close",
389
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
390
- Button,
391
- {
392
- variant: "ghost",
393
- className: "absolute top-3 right-3",
394
- size: "icon-sm"
395
- }
396
- ),
397
- children: [
398
- /* @__PURE__ */ jsxRuntimeExports.jsx(
399
- IconX,
400
- {}
401
- ),
402
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Close" })
403
- ]
404
- }
405
- )
406
- ]
407
- }
408
- )
409
- ] });
410
- }
411
- function SheetHeader({ className, ...props }) {
412
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
413
- "div",
414
- {
415
- "data-slot": "sheet-header",
416
- className: cn("flex flex-col gap-0.5 p-4", className),
417
- ...props
418
- }
419
- );
420
- }
421
- function SheetTitle({ className, ...props }) {
422
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
423
- DialogTitle,
424
- {
425
- "data-slot": "sheet-title",
426
- className: cn(
427
- "font-heading text-base font-medium text-foreground",
428
- className
429
- ),
430
- ...props
431
- }
432
- );
433
- }
434
- function SheetDescription({
435
- className,
436
- ...props
437
- }) {
438
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
439
- DialogDescription,
440
- {
441
- "data-slot": "sheet-description",
442
- className: cn("text-sm text-muted-foreground", className),
443
- ...props
444
- }
445
- );
446
- }
447
- const SIDEBAR_COOKIE_NAME = "sidebar_state";
448
- const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
449
- const SIDEBAR_WIDTH = "16rem";
450
- const SIDEBAR_WIDTH_MOBILE = "18rem";
451
- const SIDEBAR_WIDTH_ICON = "3rem";
452
- const SIDEBAR_KEYBOARD_SHORTCUT = "b";
453
- const SidebarContext = reactExports.createContext(null);
454
- function useSidebar() {
455
- const context = reactExports.useContext(SidebarContext);
456
- if (!context) {
457
- throw new Error("useSidebar must be used within a SidebarProvider.");
458
- }
459
- return context;
460
- }
461
- function SidebarProvider({
462
- defaultOpen = true,
463
- open: openProp,
464
- onOpenChange: setOpenProp,
465
- className,
466
- style,
467
- children,
468
- ...props
469
- }) {
470
- const isMobile = useIsMobile();
471
- const [openMobile, setOpenMobile] = reactExports.useState(false);
472
- const [_open, _setOpen] = reactExports.useState(defaultOpen);
473
- const open = openProp ?? _open;
474
- const setOpen = reactExports.useCallback(
475
- (value) => {
476
- const openState = typeof value === "function" ? value(open) : value;
477
- if (setOpenProp) {
478
- setOpenProp(openState);
479
- } else {
480
- _setOpen(openState);
481
- }
482
- document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
483
- },
484
- [setOpenProp, open]
485
- );
486
- const toggleSidebar = reactExports.useCallback(() => {
487
- return isMobile ? setOpenMobile((open2) => !open2) : setOpen((open2) => !open2);
488
- }, [isMobile, setOpen, setOpenMobile]);
489
- reactExports.useEffect(() => {
490
- const handleKeyDown = (event) => {
491
- if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
492
- event.preventDefault();
493
- toggleSidebar();
494
- }
495
- };
496
- window.addEventListener("keydown", handleKeyDown);
497
- return () => window.removeEventListener("keydown", handleKeyDown);
498
- }, [toggleSidebar]);
499
- const state = open ? "expanded" : "collapsed";
500
- const contextValue = reactExports.useMemo(
501
- () => ({
502
- state,
503
- open,
504
- setOpen,
505
- isMobile,
506
- openMobile,
507
- setOpenMobile,
508
- toggleSidebar
509
- }),
510
- [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
511
- );
512
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
513
- "div",
514
- {
515
- "data-slot": "sidebar-wrapper",
516
- style: {
517
- "--sidebar-width": SIDEBAR_WIDTH,
518
- "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
519
- ...style
520
- },
521
- className: cn(
522
- "group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar",
523
- className
524
- ),
525
- ...props,
526
- children
527
- }
528
- ) });
529
- }
530
- function Sidebar({
531
- side = "left",
532
- variant = "sidebar",
533
- collapsible = "offcanvas",
534
- className,
535
- children,
536
- dir,
537
- ...props
538
- }) {
539
- const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
540
- if (collapsible === "none") {
541
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
542
- "div",
543
- {
544
- "data-slot": "sidebar",
545
- className: cn(
546
- "flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground",
547
- className
548
- ),
549
- ...props,
550
- children
551
- }
552
- );
553
- }
554
- if (isMobile) {
555
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Sheet, { open: openMobile, onOpenChange: setOpenMobile, ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
556
- SheetContent,
557
- {
558
- dir,
559
- "data-sidebar": "sidebar",
560
- "data-slot": "sidebar",
561
- "data-mobile": "true",
562
- className: "w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden",
563
- style: {
564
- "--sidebar-width": SIDEBAR_WIDTH_MOBILE
565
- },
566
- side,
567
- children: [
568
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SheetHeader, { className: "sr-only", children: [
569
- /* @__PURE__ */ jsxRuntimeExports.jsx(SheetTitle, { children: "Sidebar" }),
570
- /* @__PURE__ */ jsxRuntimeExports.jsx(SheetDescription, { children: "Displays the mobile sidebar." })
571
- ] }),
572
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-full w-full flex-col", children })
573
- ]
574
- }
575
- ) });
576
- }
577
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
578
- "div",
579
- {
580
- className: "group peer hidden text-sidebar-foreground md:block",
581
- "data-state": state,
582
- "data-collapsible": state === "collapsed" ? collapsible : "",
583
- "data-variant": variant,
584
- "data-side": side,
585
- "data-slot": "sidebar",
586
- children: [
587
- /* @__PURE__ */ jsxRuntimeExports.jsx(
588
- "div",
589
- {
590
- "data-slot": "sidebar-gap",
591
- className: cn(
592
- "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
593
- "group-data-[collapsible=offcanvas]:w-0",
594
- "group-data-[side=right]:rotate-180",
595
- variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
596
- )
597
- }
598
- ),
599
- /* @__PURE__ */ jsxRuntimeExports.jsx(
600
- "div",
601
- {
602
- "data-slot": "sidebar-container",
603
- "data-side": side,
604
- className: cn(
605
- "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex",
606
- // Adjust the padding for floating and inset variants.
607
- variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
608
- className
609
- ),
610
- ...props,
611
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(
612
- "div",
613
- {
614
- "data-sidebar": "sidebar",
615
- "data-slot": "sidebar-inner",
616
- className: "flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border",
617
- children
618
- }
619
- )
620
- }
621
- )
622
- ]
623
- }
624
- );
625
- }
626
- function SidebarTrigger({
627
- className,
628
- onClick,
629
- ...props
630
- }) {
631
- const { toggleSidebar } = useSidebar();
632
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
633
- Button,
634
- {
635
- "data-sidebar": "trigger",
636
- "data-slot": "sidebar-trigger",
637
- variant: "ghost",
638
- size: "icon-sm",
639
- className: cn(className),
640
- onClick: (event) => {
641
- onClick?.(event);
642
- toggleSidebar();
643
- },
644
- ...props,
645
- children: [
646
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconLayoutSidebar, {}),
647
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Toggle Sidebar" })
648
- ]
649
- }
650
- );
651
- }
652
- function SidebarInset({ className, ...props }) {
653
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
654
- "main",
655
- {
656
- "data-slot": "sidebar-inset",
657
- className: cn(
658
- "relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
659
- className
660
- ),
661
- ...props
662
- }
663
- );
664
- }
665
- function SidebarHeader({ className, ...props }) {
666
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
667
- "div",
668
- {
669
- "data-slot": "sidebar-header",
670
- "data-sidebar": "header",
671
- className: cn("flex flex-col gap-2 p-2", className),
672
- ...props
673
- }
674
- );
675
- }
676
- function SidebarContent({ className, ...props }) {
677
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
678
- "div",
679
- {
680
- "data-slot": "sidebar-content",
681
- "data-sidebar": "content",
682
- className: cn(
683
- "no-scrollbar flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
684
- className
685
- ),
686
- ...props
687
- }
688
- );
689
- }
690
- function SidebarGroup({ className, ...props }) {
691
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
692
- "div",
693
- {
694
- "data-slot": "sidebar-group",
695
- "data-sidebar": "group",
696
- className: cn("relative flex w-full min-w-0 flex-col p-2", className),
697
- ...props
698
- }
699
- );
700
- }
701
- function SidebarGroupLabel({
702
- className,
703
- render,
704
- ...props
705
- }) {
706
- return useRender({
707
- defaultTagName: "div",
708
- props: mergeProps(
709
- {
710
- className: cn(
711
- "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
712
- className
713
- )
714
- },
715
- props
716
- ),
717
- render,
718
- state: {
719
- slot: "sidebar-group-label",
720
- sidebar: "group-label"
721
- }
722
- });
723
- }
724
- function SidebarMenu({ className, ...props }) {
725
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
726
- "ul",
727
- {
728
- "data-slot": "sidebar-menu",
729
- "data-sidebar": "menu",
730
- className: cn("flex w-full min-w-0 flex-col gap-0", className),
731
- ...props
732
- }
733
- );
734
- }
735
- function SidebarMenuItem({ className, ...props }) {
736
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
737
- "li",
738
- {
739
- "data-slot": "sidebar-menu-item",
740
- "data-sidebar": "menu-item",
741
- className: cn("group/menu-item relative", className),
742
- ...props
743
- }
744
- );
745
- }
746
- const sidebarMenuButtonVariants = cva(
747
- "peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
748
- {
749
- variants: {
750
- variant: {
751
- default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
752
- outline: "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
753
- },
754
- size: {
755
- default: "h-8 text-sm",
756
- sm: "h-7 text-xs",
757
- lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!"
758
- }
759
- },
760
- defaultVariants: {
761
- variant: "default",
762
- size: "default"
763
- }
764
- }
765
- );
766
- function SidebarMenuButton({
767
- render,
768
- isActive = false,
769
- variant = "default",
770
- size = "default",
771
- tooltip,
772
- className,
773
- ...props
774
- }) {
775
- const { isMobile, state } = useSidebar();
776
- const comp = useRender({
777
- defaultTagName: "button",
778
- props: mergeProps(
779
- {
780
- className: cn(sidebarMenuButtonVariants({ variant, size }), className)
781
- },
782
- props
783
- ),
784
- render: !tooltip ? render : /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { render }),
785
- state: {
786
- slot: "sidebar-menu-button",
787
- sidebar: "menu-button",
788
- size,
789
- active: isActive
790
- }
791
- });
792
- if (!tooltip) {
793
- return comp;
794
- }
795
- if (typeof tooltip === "string") {
796
- tooltip = {
797
- children: tooltip
798
- };
799
- }
800
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
801
- comp,
802
- /* @__PURE__ */ jsxRuntimeExports.jsx(
803
- TooltipContent,
804
- {
805
- side: "right",
806
- align: "center",
807
- hidden: state !== "collapsed" || isMobile,
808
- ...tooltip
809
- }
810
- )
811
- ] });
812
- }
813
- function SidebarMenuSub({ className, ...props }) {
814
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
815
- "ul",
816
- {
817
- "data-slot": "sidebar-menu-sub",
818
- "data-sidebar": "menu-sub",
819
- className: cn(
820
- "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5 group-data-[collapsible=icon]:hidden",
821
- className
822
- ),
823
- ...props
824
- }
825
- );
826
- }
827
- function SidebarMenuSubButton({
828
- render,
829
- size = "md",
830
- isActive = false,
831
- className,
832
- ...props
833
- }) {
834
- return useRender({
835
- defaultTagName: "a",
836
- props: mergeProps(
837
- {
838
- className: cn(
839
- "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
840
- className
841
- )
842
- },
843
- props
844
- ),
845
- render,
846
- state: {
847
- slot: "sidebar-menu-sub-button",
848
- sidebar: "menu-sub-button",
849
- size,
850
- active: isActive
851
- }
852
- });
853
- }
854
- function Collapsible({ ...props }) {
855
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleRoot, { "data-slot": "collapsible", ...props });
856
- }
857
- function CollapsibleTrigger({ ...props }) {
858
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleTrigger$1, { "data-slot": "collapsible-trigger", ...props });
859
- }
860
- function CollapsibleContent({ ...props }) {
861
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsiblePanel, { "data-slot": "collapsible-content", ...props });
862
- }
863
- function isRouteActive(pathname, link) {
864
- if (pathname === link) return true;
865
- if (link !== "/" && pathname.startsWith(link)) return true;
866
- return false;
867
- }
868
- function DashboardNavigation({ routes }) {
869
- const { state } = useSidebar();
870
- const isCollapsed = state === "collapsed";
871
- const [openCollapsible, setOpenCollapsible] = reactExports.useState(null);
872
- const location = useLocation();
873
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMenu, { children: routes.map((route) => {
874
- const isOpen = !isCollapsed && openCollapsible === route.id;
875
- const hasSubRoutes = !!route.subs?.length;
876
- const isActive = isRouteActive(location.pathname, route.link) || route.subs?.some((sub) => isRouteActive(location.pathname, sub.link));
877
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMenuItem, { children: hasSubRoutes ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
878
- Collapsible,
879
- {
880
- open: isOpen,
881
- onOpenChange: (open) => setOpenCollapsible(open ? route.id : null),
882
- className: "w-full",
883
- children: [
884
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
885
- CollapsibleTrigger,
886
- {
887
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
888
- SidebarMenuButton,
889
- {
890
- className: cn(
891
- "flex w-full items-center rounded-lg px-2 transition-colors",
892
- isOpen || isActive ? "bg-sidebar-muted text-foreground" : "text-muted-foreground hover:bg-sidebar-muted hover:text-foreground",
893
- isCollapsed && "justify-center"
894
- )
895
- }
896
- ),
897
- children: [
898
- route.icon,
899
- !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-2 flex-1 text-sm font-medium", children: route.title }),
900
- !isCollapsed && hasSubRoutes && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-auto", children: isOpen ? /* @__PURE__ */ jsxRuntimeExports.jsx(IconChevronUp, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(IconChevronDown, { className: "size-4" }) })
901
- ]
902
- }
903
- ),
904
- !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMenuSub, { className: "my-1 ml-3.5", children: route.subs?.map((subRoute) => {
905
- const isSubActive = isRouteActive(location.pathname, subRoute.link);
906
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
907
- SidebarMenuItem,
908
- {
909
- className: "h-auto",
910
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(
911
- SidebarMenuSubButton,
912
- {
913
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
914
- Link,
915
- {
916
- to: subRoute.link,
917
- className: cn(
918
- "flex items-center rounded-md px-4 py-1.5 text-sm font-medium",
919
- isSubActive ? "bg-sidebar-muted text-foreground" : "text-muted-foreground hover:bg-sidebar-muted hover:text-foreground"
920
- )
921
- }
922
- ),
923
- children: subRoute.title
924
- }
925
- )
926
- },
927
- `${route.id}-${subRoute.title}`
928
- );
929
- }) }) })
930
- ]
931
- }
932
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
933
- SidebarMenuButton,
934
- {
935
- tooltip: route.title,
936
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
937
- Link,
938
- {
939
- to: route.link,
940
- className: cn(
941
- "flex items-center rounded-lg px-2 transition-colors",
942
- isActive ? "bg-sidebar-muted text-foreground" : "text-muted-foreground hover:bg-sidebar-muted hover:text-foreground",
943
- isCollapsed && "justify-center"
944
- )
945
- }
946
- ),
947
- children: [
948
- route.icon,
949
- !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-2 text-sm font-medium", children: route.title })
950
- ]
951
- }
952
- ) }, route.id);
953
- }) });
954
- }
955
- function DropdownMenu({ ...props }) {
956
- return /* @__PURE__ */ jsxRuntimeExports.jsx(MenuRoot, { "data-slot": "dropdown-menu", ...props });
957
- }
958
- function DropdownMenuTrigger({ ...props }) {
959
- return /* @__PURE__ */ jsxRuntimeExports.jsx(MenuTrigger, { "data-slot": "dropdown-menu-trigger", ...props });
960
- }
961
- function DropdownMenuContent({
962
- align = "start",
963
- alignOffset = 0,
964
- side = "bottom",
965
- sideOffset = 4,
966
- className,
967
- ...props
968
- }) {
969
- return /* @__PURE__ */ jsxRuntimeExports.jsx(MenuPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
970
- MenuPositioner,
971
- {
972
- className: "isolate z-50 outline-none",
973
- align,
974
- alignOffset,
975
- side,
976
- sideOffset,
977
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(
978
- MenuPopup,
979
- {
980
- "data-slot": "dropdown-menu-content",
981
- className: cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!", className),
982
- ...props
983
- }
984
- )
985
- }
986
- ) });
987
- }
988
- function DropdownMenuItem({
989
- className,
990
- inset,
991
- variant = "default",
992
- ...props
993
- }) {
994
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
995
- MenuItem,
996
- {
997
- "data-slot": "dropdown-menu-item",
998
- "data-inset": inset,
999
- "data-variant": variant,
1000
- className: cn(
1001
- "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
1002
- className
1003
- ),
1004
- ...props
1005
- }
1006
- );
1007
- }
1008
- const BASE_URL = "http://localhost:47474";
1009
- class ApiError extends Error {
1010
- constructor(message, status, code) {
1011
- super(message);
1012
- this.status = status;
1013
- this.code = code;
1014
- this.name = "ApiError";
1015
- }
1016
- status;
1017
- code;
1018
- }
1019
- async function handleResponse(res) {
1020
- if (!res.ok) {
1021
- let body = null;
1022
- try {
1023
- body = await res.json();
1024
- } catch {
1025
- }
1026
- throw new ApiError(
1027
- (body && typeof body.error === "string" ? body.error : res.statusText) ?? "Unknown error",
1028
- res.status,
1029
- typeof body?.code === "string" ? body.code : void 0
1030
- );
1031
- }
1032
- return res.json();
1033
- }
1034
- async function get(path, params) {
1035
- const url = new URL(`${BASE_URL}${path}`);
1036
- const res = await fetch(url, { credentials: "include", cache: "no-store" });
1037
- return handleResponse(res);
1038
- }
1039
- async function post(path, body) {
1040
- const res = await fetch(`${BASE_URL}${path}`, {
1041
- method: "POST",
1042
- headers: { "Content-Type": "application/json" },
1043
- credentials: "include",
1044
- body: body != null ? JSON.stringify(body) : void 0
1045
- });
1046
- return handleResponse(res);
1047
- }
1048
- async function put(path, body) {
1049
- const res = await fetch(`${BASE_URL}${path}`, {
1050
- method: "PUT",
1051
- headers: { "Content-Type": "application/json" },
1052
- credentials: "include",
1053
- body: body != null ? JSON.stringify(body) : void 0
1054
- });
1055
- return handleResponse(res);
1056
- }
1057
- async function patch(path, body) {
1058
- const res = await fetch(`${BASE_URL}${path}`, {
1059
- method: "PATCH",
1060
- headers: { "Content-Type": "application/json" },
1061
- credentials: "include",
1062
- body: body != null ? JSON.stringify(body) : void 0
1063
- });
1064
- return handleResponse(res);
1065
- }
1066
- async function del(path) {
1067
- const res = await fetch(`${BASE_URL}${path}`, {
1068
- method: "DELETE",
1069
- credentials: "include"
1070
- });
1071
- return handleResponse(res);
1072
- }
1073
- function uid() {
1074
- return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
1075
- }
1076
- const ACTIVE_SESSION_KEY = "hermium_active_session";
1077
- const PINNED_SESSIONS_KEY = "hermium_pinned_sessions";
1078
- function loadPinnedSessions() {
1079
- try {
1080
- const raw = localStorage.getItem(PINNED_SESSIONS_KEY);
1081
- return raw ? JSON.parse(raw) : [];
1082
- } catch {
1083
- return [];
1084
- }
1085
- }
1086
- function savePinnedSessions(ids) {
1087
- localStorage.setItem(PINNED_SESSIONS_KEY, JSON.stringify(ids));
1088
- }
1089
- const initialState = {
1090
- sessions: [],
1091
- activeSessionId: null,
1092
- streamingSessions: /* @__PURE__ */ new Set(),
1093
- isLoadingSessions: false,
1094
- isLoadingMessages: false,
1095
- sessionsLoaded: false,
1096
- pinnedSessionIds: loadPinnedSessions()
1097
- };
1098
- let _loadingSessionId = null;
1099
- const useChatStore = create()(
1100
- devtools(
1101
- (set, get$1) => ({
1102
- ...initialState,
1103
- getSession: (id) => get$1().sessions.find((s) => s.id === id),
1104
- activeSession: () => {
1105
- const st = get$1();
1106
- return st.sessions.find((s) => s.id === st.activeSessionId) || null;
1107
- },
1108
- messages: () => {
1109
- const st = get$1();
1110
- const session = st.sessions.find((s) => s.id === st.activeSessionId);
1111
- return session?.messages || [];
1112
- },
1113
- updateSession: (id, update) => {
1114
- set((s) => ({
1115
- sessions: s.sessions.map(
1116
- (sx) => sx.id === id ? { ...sx, ...update } : sx
1117
- )
1118
- }));
1119
- },
1120
- loadSessions: async () => {
1121
- try {
1122
- set({ isLoadingSessions: true });
1123
- const data = await get("/api/sessions");
1124
- const apiSessions = (data.sessions || []).map((s) => {
1125
- const startedAt = s.started_at ? s.started_at < 1e12 ? s.started_at * 1e3 : s.started_at : Date.now();
1126
- return {
1127
- id: String(s.id),
1128
- title: s.title || "Untitled",
1129
- model: s.model || "",
1130
- source: s.source || "webui",
1131
- messages: [],
1132
- createdAt: startedAt,
1133
- updatedAt: s.ended_at ? s.ended_at < 1e12 ? s.ended_at * 1e3 : s.ended_at : startedAt,
1134
- messageCount: s.message_count ?? 0,
1135
- inputTokens: s.input_tokens ?? 0,
1136
- outputTokens: s.output_tokens ?? 0
1137
- };
1138
- });
1139
- const deduped = /* @__PURE__ */ new Map();
1140
- for (const s of apiSessions) deduped.set(s.id, s);
1141
- const apiSessionsDeduped = [...deduped.values()];
1142
- const preservedLocals = get$1().sessions.filter(
1143
- (local) => !apiSessionsDeduped.some((api) => api.id === local.id) && (local.messages.length > 0 || local.id === get$1().activeSessionId)
1144
- );
1145
- const merged = [...preservedLocals, ...apiSessionsDeduped];
1146
- set({ sessions: merged, sessionsLoaded: true, streamingSessions: /* @__PURE__ */ new Set() });
1147
- const currentActive = get$1().activeSessionId;
1148
- if (!currentActive) {
1149
- const savedId = localStorage.getItem(ACTIVE_SESSION_KEY);
1150
- if (savedId && apiSessionsDeduped.some((s) => s.id === savedId)) {
1151
- get$1().switchSession(savedId);
1152
- }
1153
- }
1154
- } catch (err) {
1155
- console.error("Failed to load sessions:", err);
1156
- } finally {
1157
- set({ isLoadingSessions: false });
1158
- }
1159
- },
1160
- switchSession: (id) => {
1161
- const current = get$1().activeSessionId;
1162
- if (current === id) return;
1163
- set({ activeSessionId: id });
1164
- localStorage.setItem(ACTIVE_SESSION_KEY, id);
1165
- },
1166
- loadSessionMessages: async (id) => {
1167
- _loadingSessionId = id;
1168
- try {
1169
- set({ isLoadingMessages: true });
1170
- const data = await get(`/api/sessions/${id}`);
1171
- if (_loadingSessionId !== id) return;
1172
- const rawMessages = data.messages || [];
1173
- const msgs = rawMessages.map((m, idx) => ({
1174
- id: String(m.id || `${id}-${idx}`),
1175
- role: m.role,
1176
- content: typeof m.content === "string" ? m.content : "",
1177
- timestamp: m.timestamp ? Number(m.timestamp) * (m.timestamp < 1e12 ? 1e3 : 1) : Date.now(),
1178
- toolCalls: m.tool_calls ? m.tool_calls.map((tc) => ({
1179
- name: tc.function?.name || tc.name || "tool",
1180
- preview: "",
1181
- status: "done",
1182
- callId: tc.id || void 0,
1183
- output: void 0
1184
- })) : void 0,
1185
- reasoning: m.reasoning || m.reasoning_content || void 0
1186
- }));
1187
- const title = data.title || msgs.find((m) => m.role === "user")?.content?.slice(0, 64) || "Untitled";
1188
- set((s) => {
1189
- const existing = s.sessions.find((sx) => sx.id === id);
1190
- if (existing) {
1191
- const mergedMessages = msgs.length === 0 && existing.messages.length > 0 ? existing.messages : msgs;
1192
- return {
1193
- sessions: s.sessions.map(
1194
- (sx) => sx.id === id ? {
1195
- ...sx,
1196
- messages: mergedMessages,
1197
- title,
1198
- model: sx.model || data.model || "",
1199
- messageCount: data.message_count ?? sx.messageCount ?? msgs.length,
1200
- inputTokens: data.input_tokens ?? sx.inputTokens ?? 0,
1201
- outputTokens: data.output_tokens ?? sx.outputTokens ?? 0,
1202
- reasoningTokens: data.reasoning_tokens ?? sx.reasoningTokens ?? 0
1203
- } : sx
1204
- )
1205
- };
1206
- }
1207
- return {
1208
- sessions: [
1209
- ...s.sessions,
1210
- {
1211
- id,
1212
- title,
1213
- model: data.model || "",
1214
- source: data.source || "webui",
1215
- messages: msgs,
1216
- createdAt: data.started_at ? data.started_at < 1e12 ? data.started_at * 1e3 : data.started_at : Date.now(),
1217
- updatedAt: data.ended_at ? data.ended_at < 1e12 ? data.ended_at * 1e3 : data.ended_at : Date.now(),
1218
- messageCount: data.message_count ?? msgs.length,
1219
- inputTokens: data.input_tokens ?? 0,
1220
- outputTokens: data.output_tokens ?? 0,
1221
- reasoningTokens: data.reasoning_tokens ?? 0
1222
- }
1223
- ]
1224
- };
1225
- });
1226
- } finally {
1227
- if (_loadingSessionId === id) _loadingSessionId = null;
1228
- set({ isLoadingMessages: false });
1229
- }
1230
- },
1231
- deleteSession: (id) => {
1232
- set((s) => ({
1233
- sessions: s.sessions.filter((sx) => sx.id !== id)
1234
- }));
1235
- },
1236
- newChat: async (model) => {
1237
- const sessionId = uid();
1238
- const now = Date.now();
1239
- const session = {
1240
- id: sessionId,
1241
- title: "New Conversation",
1242
- model: model || "",
1243
- messages: [],
1244
- createdAt: now,
1245
- updatedAt: now
1246
- };
1247
- set((s) => ({
1248
- sessions: [session, ...s.sessions],
1249
- activeSessionId: session.id
1250
- }));
1251
- localStorage.setItem(ACTIVE_SESSION_KEY, session.id);
1252
- return session.id;
1253
- },
1254
- addOrUpdateSession: (session) => {
1255
- set((s) => {
1256
- const idx = s.sessions.findIndex((sx) => sx.id === session.id);
1257
- if (idx >= 0) {
1258
- const updated = [...s.sessions];
1259
- updated[idx] = { ...updated[idx], ...session };
1260
- return { sessions: updated };
1261
- }
1262
- return { sessions: [session, ...s.sessions] };
1263
- });
1264
- },
1265
- togglePin: (sessionId) => {
1266
- set((s) => {
1267
- const isPinned = s.pinnedSessionIds.includes(sessionId);
1268
- const next = isPinned ? s.pinnedSessionIds.filter((id) => id !== sessionId) : [...s.pinnedSessionIds, sessionId];
1269
- savePinnedSessions(next);
1270
- return { pinnedSessionIds: next };
1271
- });
1272
- },
1273
- isSessionStreaming: (id) => {
1274
- return get$1().streamingSessions.has(id);
1275
- }
1276
- }),
1277
- { name: "chat", enabled: false }
1278
- )
1279
- );
1280
- async function createSession(params) {
1281
- const data = await post("/api/sessions", { model: params?.model });
1282
- return {
1283
- id: data.id,
1284
- session_id: data.id,
1285
- title: data.title || "New Conversation",
1286
- workspace: params?.workspace || "",
1287
- model: data.model || "",
1288
- source: data.source || "webui",
1289
- message_count: data.message_count ?? 0,
1290
- started_at: data.started_at,
1291
- ended_at: null,
1292
- created_at: data.started_at,
1293
- updated_at: data.started_at,
1294
- input_tokens: data.input_tokens ?? 0,
1295
- output_tokens: data.output_tokens ?? 0
1296
- };
1297
- }
1298
- async function renameSession(sessionId, title) {
1299
- return patch(`/api/sessions/${sessionId}`, { title });
1300
- }
1301
- async function deleteSessionApi(sessionId) {
1302
- return del(`/api/sessions/${sessionId}`);
1303
- }
1304
- async function fetchModels() {
1305
- return get("/api/config/models");
1306
- }
1307
- async function updateModelConfig(params) {
1308
- return post("/api/config/model", params);
1309
- }
1310
- async function respondApproval(params) {
1311
- return post("/api/approval/respond", params);
1312
- }
1313
- async function fetchVersion() {
1314
- return get("/api/version");
1315
- }
1316
- async function triggerUpdate() {
1317
- return post("/api/update");
1318
- }
1319
- function useVersionCheck() {
1320
- return useQuery({
1321
- queryKey: ["version"],
1322
- queryFn: fetchVersion,
1323
- staleTime: 5 * 60 * 1e3,
1324
- // 5 minutes
1325
- refetchInterval: 5 * 60 * 1e3,
1326
- // poll every 5 minutes
1327
- refetchIntervalInBackground: false,
1328
- retry: 2
1329
- });
1330
- }
1331
- function useTriggerUpdate() {
1332
- return useMutation({
1333
- mutationFn: triggerUpdate
1334
- });
1335
- }
1336
- function normalizeContent(raw) {
1337
- if (typeof raw === "string") return raw;
1338
- if (Array.isArray(raw)) {
1339
- return raw.map((p) => typeof p === "object" && p.text ? p.text : "").join(" ");
1340
- }
1341
- return "";
1342
- }
1343
- function transformMessage(m, sessionId, index) {
1344
- const role = m.role;
1345
- const content = normalizeContent(m.content);
1346
- const reasoning = m.reasoning || m.reasoning_content || "";
1347
- const toolCalls = [];
1348
- if (Array.isArray(m.tool_calls)) {
1349
- for (const tc of m.tool_calls) {
1350
- const fn = tc.function;
1351
- const tcName = fn?.name || tc.name;
1352
- if (tcName) {
1353
- toolCalls.push({ name: tcName, preview: "", status: "done" });
1354
- }
1355
- }
1356
- }
1357
- let displayRole = "assistant";
1358
- if (role === "user") displayRole = "user";
1359
- if (role === "tool") displayRole = "tool";
1360
- return {
1361
- id: `${sessionId}-${index}`,
1362
- role: displayRole,
1363
- content,
1364
- timestamp: m.timestamp ? Number(m.timestamp) * (m.timestamp < 1e12 ? 1e3 : 1) : Date.now(),
1365
- reasoning: reasoning || void 0,
1366
- toolName: m.name || void 0,
1367
- toolCalls: toolCalls.length > 0 ? toolCalls : void 0
1368
- };
1369
- }
1370
- const sessionMsgKeys = {
1371
- all: ["session-messages"],
1372
- detail: (id) => [...sessionMsgKeys.all, id]
1373
- };
1374
- function prefetchSessionMessages(queryClient2, sessionId) {
1375
- return queryClient2.prefetchQuery({
1376
- queryKey: sessionMsgKeys.detail(sessionId),
1377
- queryFn: async () => {
1378
- const data = await get(
1379
- `/api/sessions/${sessionId}`
1380
- );
1381
- const msgs = [];
1382
- let derivedTitle = data.title || "";
1383
- for (const m of data.messages ?? []) {
1384
- const msg = transformMessage(m, sessionId, msgs.length);
1385
- msgs.push(msg);
1386
- if (!derivedTitle && m.role === "user" && msg.content.trim()) {
1387
- derivedTitle = msg.content.trim();
1388
- }
1389
- }
1390
- return { messages: msgs, title: derivedTitle || "Conversation" };
1391
- },
1392
- staleTime: 3e4,
1393
- gcTime: 3e5
1394
- });
1395
- }
1396
- const dashboardRoutes = [
1397
- {
1398
- id: "dashboard",
1399
- title: "New Chat",
1400
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconCirclePlus, { className: "size-4" }),
1401
- link: "/"
1402
- },
1403
- {
1404
- id: "memory",
1405
- title: "Memory",
1406
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconBrain, { className: "size-4" }),
1407
- link: "/memory"
1408
- },
1409
- {
1410
- id: "skills",
1411
- title: "Skills",
1412
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconPuzzle, { className: "size-4" }),
1413
- link: "/skills"
1414
- },
1415
- {
1416
- id: "automations",
1417
- title: "Automations",
1418
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconRobot, { className: "size-4" }),
1419
- link: "/automations"
1420
- },
1421
- {
1422
- id: "channels",
1423
- title: "Channels",
1424
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconHash, { className: "size-4" }),
1425
- link: "/channels"
1426
- },
1427
- {
1428
- id: "usage",
1429
- title: "Usage",
1430
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconChartBar, { className: "size-4" }),
1431
- link: "/usage"
1432
- }
1433
- ];
1434
- function isSettingsPath(pathname) {
1435
- return pathname.startsWith("/settings");
1436
- }
1437
- function DashboardSidebar() {
1438
- const { state } = useSidebar();
1439
- const isCollapsed = state === "collapsed";
1440
- const { resolvedTheme, setTheme } = useTheme();
1441
- const queryClient2 = useQueryClient();
1442
- const sessions = useChatStore((s) => s.sessions);
1443
- const loadSessions = useChatStore((s) => s.loadSessions);
1444
- const activeSessionId = useChatStore((s) => s.activeSessionId);
1445
- const deleteStoreSession = useChatStore((s) => s.deleteSession);
1446
- const sessionsLoaded = useChatStore((s) => s.sessionsLoaded);
1447
- const pinnedIds = useChatStore((s) => s.pinnedSessionIds);
1448
- const togglePin = useChatStore((s) => s.togglePin);
1449
- const prefetchSession = reactExports.useCallback(
1450
- (sid) => prefetchSessionMessages(queryClient2, sid),
1451
- [queryClient2]
1452
- );
1453
- const location = useLocation();
1454
- const currentSection = new URLSearchParams(location.search).get("section") || "appearance";
1455
- const [searchQuery, setSearchQuery] = reactExports.useState("");
1456
- const [isSearchOpen, setIsSearchOpen] = reactExports.useState(false);
1457
- const [activePanel, setActivePanel] = reactExports.useState(
1458
- isSettingsPath(location.pathname) ? "settings" : "main"
1459
- );
1460
- const searchRef = reactExports.useRef(null);
1461
- reactExports.useEffect(() => {
1462
- setActivePanel(isSettingsPath(location.pathname) ? "settings" : "main");
1463
- }, [location.pathname]);
1464
- reactExports.useEffect(() => {
1465
- console.log("[Sidebar] mount effect, sessionsLoaded=", sessionsLoaded);
1466
- if (!sessionsLoaded) {
1467
- loadSessions();
1468
- }
1469
- }, [sessionsLoaded, loadSessions]);
1470
- const [editingSessionId, setEditingSessionId] = reactExports.useState(null);
1471
- const [editTitle, setEditTitle] = reactExports.useState("");
1472
- const renameInputRef = reactExports.useRef(null);
1473
- const handleDeleteSession = async (sid) => {
1474
- try {
1475
- await deleteSessionApi(sid);
1476
- deleteStoreSession(sid);
1477
- } catch {
1478
- }
1479
- };
1480
- const handleRename = async (sid) => {
1481
- const trimmed = editTitle.trim();
1482
- if (!trimmed) {
1483
- setEditingSessionId(null);
1484
- return;
1485
- }
1486
- try {
1487
- await renameSession(sid, trimmed);
1488
- useChatStore.getState().updateSession(sid, { title: trimmed });
1489
- } catch {
1490
- }
1491
- setEditingSessionId(null);
1492
- };
1493
- const handleCopyId = (sid) => {
1494
- navigator.clipboard.writeText(sid).catch(() => {
1495
- });
1496
- };
1497
- const startRename = (s) => {
1498
- setEditingSessionId(s.id);
1499
- setEditTitle(s.title);
1500
- setTimeout(() => renameInputRef.current?.focus(), 0);
1501
- };
1502
- const visibleSessions = sessions.filter(
1503
- (s) => s.messages.length > 0 || (s.messageCount ?? 0) > 0 || s.id === activeSessionId
1504
- );
1505
- const filteredSessions = searchQuery ? visibleSessions.filter(
1506
- (s) => s.title.toLowerCase().includes(searchQuery.toLowerCase())
1507
- ) : visibleSessions;
1508
- const pinnedSessions = filteredSessions.filter((s) => pinnedIds.includes(s.id));
1509
- const unpinnedSessions = filteredSessions.filter((s) => !pinnedIds.includes(s.id));
1510
- const openSearch = () => {
1511
- setIsSearchOpen(true);
1512
- setTimeout(() => searchRef.current?.focus(), 0);
1513
- };
1514
- const closeSearch = () => {
1515
- setIsSearchOpen(false);
1516
- setSearchQuery("");
1517
- };
1518
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(Sidebar, { variant: "inset", collapsible: "icon", children: [
1519
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1520
- SidebarHeader,
1521
- {
1522
- className: cn(
1523
- "flex px-0.5 md:pt-3.5",
1524
- isCollapsed ? "flex-row items-center justify-between gap-y-4 md:flex-col md:items-start md:justify-start" : "flex-row items-center justify-between"
1525
- ),
1526
- children: [
1527
- /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/", className: "flex items-center px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: "/nous-logo.png", alt: "Nous", className: "h-6 w-6 rounded object-cover shrink-0" }) }),
1528
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("flex items-center gap-2", isCollapsed ? "flex-row md:flex-col-reverse" : "flex-row"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarTrigger, {}) })
1529
- ]
1530
- }
1531
- ),
1532
- /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative flex flex-col flex-1 min-h-0 gap-1 px-0.5 py-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1533
- "div",
1534
- {
1535
- className: "flex h-full transition-transform duration-150 ease-out",
1536
- style: {
1537
- width: "200%",
1538
- transform: activePanel === "settings" ? "translateX(-50%)" : "translateX(0)"
1539
- },
1540
- children: [
1541
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 h-full justify-between w-1/2", children: [
1542
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 flex-1 min-h-0", children: [
1543
- /* @__PURE__ */ jsxRuntimeExports.jsx(DashboardNavigation, { routes: dashboardRoutes }),
1544
- !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsxs(SidebarGroup, { className: "px-0 flex flex-col min-h-0", children: [
1545
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "group/search flex items-center justify-between px-0.5 pb-0.5 shrink-0", children: isSearchOpen ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex w-full items-center gap-1 min-w-0", children: [
1546
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconSearch, { className: "size-3.5 shrink-0 text-muted-foreground" }),
1547
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1548
- "input",
1549
- {
1550
- ref: searchRef,
1551
- value: searchQuery,
1552
- onChange: (e) => setSearchQuery(e.target.value),
1553
- onKeyDown: (e) => {
1554
- if (e.key === "Escape") closeSearch();
1555
- },
1556
- placeholder: "Search...",
1557
- className: "flex-1 min-w-0 bg-transparent border-none outline-none text-xs text-foreground placeholder:text-muted-foreground/50"
1558
- }
1559
- ),
1560
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1561
- "button",
1562
- {
1563
- onClick: closeSearch,
1564
- className: "flex shrink-0 items-center justify-center rounded-md p-1 text-muted-foreground hover:text-foreground hover:bg-sidebar-muted transition-colors",
1565
- "aria-label": "Close search",
1566
- title: "Close search",
1567
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(IconX, { className: "size-4" })
1568
- }
1569
- )
1570
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1571
- /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarGroupLabel, { className: "!text-[11px] !font-normal text-muted-foreground/60", children: "Chats" }),
1572
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1573
- "button",
1574
- {
1575
- onClick: openSearch,
1576
- className: "flex shrink-0 items-center justify-center rounded p-0.5 text-muted-foreground opacity-0 hover:text-foreground group-hover/search:opacity-100 transition-opacity",
1577
- "aria-label": "Search chats",
1578
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(IconSearch, { className: "size-3.5" })
1579
- }
1580
- )
1581
- ] }) }),
1582
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(SidebarMenu, { children: [
1583
- filteredSessions.length === 0 && searchQuery && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "px-2 py-4 text-xs text-muted-foreground", children: [
1584
- 'No chats match "',
1585
- searchQuery,
1586
- '"'
1587
- ] }),
1588
- filteredSessions.length === 0 && !searchQuery && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "px-2 py-4 text-xs text-muted-foreground", children: "No conversations yet. Start chatting!" }),
1589
- pinnedSessions.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(
1590
- SessionItem,
1591
- {
1592
- session: s,
1593
- isActive: activeSessionId === s.id,
1594
- isEditing: editingSessionId === s.id,
1595
- editTitle,
1596
- setEditTitle,
1597
- renameInputRef,
1598
- onRename: (sid) => handleRename(sid),
1599
- onCancelRename: () => setEditingSessionId(null),
1600
- onPrefetch: () => prefetchSession(s.id),
1601
- onStartRename: () => startRename(s),
1602
- onTogglePin: () => togglePin(s.id),
1603
- onCopyId: () => handleCopyId(s.id),
1604
- onDelete: () => handleDeleteSession(s.id),
1605
- isPinned: true
1606
- },
1607
- s.id
1608
- )),
1609
- unpinnedSessions.slice(0, 20).map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(
1610
- SessionItem,
1611
- {
1612
- session: s,
1613
- isActive: activeSessionId === s.id,
1614
- isEditing: editingSessionId === s.id,
1615
- editTitle,
1616
- setEditTitle,
1617
- renameInputRef,
1618
- onRename: (sid) => handleRename(sid),
1619
- onCancelRename: () => setEditingSessionId(null),
1620
- onPrefetch: () => prefetchSession(s.id),
1621
- onStartRename: () => startRename(s),
1622
- onTogglePin: () => togglePin(s.id),
1623
- onCopyId: () => handleCopyId(s.id),
1624
- onDelete: () => handleDeleteSession(s.id)
1625
- },
1626
- s.id
1627
- ))
1628
- ] }) })
1629
- ] })
1630
- ] }),
1631
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "shrink-0 space-y-1", children: [
1632
- /* @__PURE__ */ jsxRuntimeExports.jsx(UpdateBanner, { isCollapsed }),
1633
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("flex items-center", isCollapsed ? "flex-col gap-1" : "flex-row gap-1"), children: [
1634
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1635
- Link,
1636
- {
1637
- to: "/settings",
1638
- className: cn(
1639
- "flex items-center gap-1.5 rounded-md text-xs font-medium text-muted-foreground hover:bg-sidebar-muted hover:text-foreground transition-colors",
1640
- isCollapsed ? "justify-center p-1.5 w-full" : "flex-1 px-2 py-1"
1641
- ),
1642
- "aria-label": "Settings",
1643
- title: isCollapsed ? "Settings" : void 0,
1644
- children: [
1645
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconSettings, { className: "size-3.5 shrink-0" }),
1646
- !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Settings" })
1647
- ]
1648
- }
1649
- ),
1650
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1651
- "button",
1652
- {
1653
- onClick: () => setTheme(resolvedTheme === "dark" ? "light" : "dark"),
1654
- className: cn(
1655
- "flex items-center justify-center rounded-md text-muted-foreground hover:bg-sidebar-muted hover:text-foreground transition-colors",
1656
- isCollapsed ? "p-1.5 w-full" : "p-1.5"
1657
- ),
1658
- "aria-label": "Toggle theme",
1659
- title: isCollapsed ? resolvedTheme === "dark" ? "Light mode" : "Dark mode" : void 0,
1660
- children: [
1661
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconSun, { className: "size-3.5 dark:hidden" }),
1662
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconMoon, { className: "size-3.5 hidden dark:block" })
1663
- ]
1664
- }
1665
- )
1666
- ] })
1667
- ] })
1668
- ] }),
1669
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full justify-between w-1/2", children: [
1670
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [
1671
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-1 pb-1.5 shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground/60", children: "Settings" }) }),
1672
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto min-h-0 space-y-0.5 px-0.5", children: [
1673
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1674
- SettingsGroup,
1675
- {
1676
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconPaint, { className: "size-4" }),
1677
- title: "Appearance",
1678
- section: "appearance",
1679
- activeSection: currentSection
1680
- }
1681
- ),
1682
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1683
- SettingsGroup,
1684
- {
1685
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconBrandGithub, { className: "size-4" }),
1686
- title: "About",
1687
- section: "about",
1688
- activeSection: currentSection
1689
- }
1690
- )
1691
- ] })
1692
- ] }),
1693
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 px-0.5 pb-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1694
- Link,
1695
- {
1696
- to: "/",
1697
- className: "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm text-muted-foreground hover:bg-sidebar-muted hover:text-foreground transition-colors",
1698
- children: [
1699
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconArrowLeft, { className: "size-4 shrink-0" }),
1700
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Back to app" })
1701
- ]
1702
- }
1703
- ) })
1704
- ] })
1705
- ]
1706
- }
1707
- ) }) }) })
1708
- ] });
1709
- }
1710
- function SettingsGroup({
1711
- icon,
1712
- title,
1713
- section,
1714
- activeSection
1715
- }) {
1716
- const isActive = activeSection === section;
1717
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1718
- Link,
1719
- {
1720
- to: "/settings",
1721
- search: { section },
1722
- className: cn(
1723
- "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors",
1724
- isActive ? "bg-sidebar-muted text-foreground font-medium" : "text-muted-foreground hover:bg-sidebar-muted hover:text-foreground"
1725
- ),
1726
- children: [
1727
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex shrink-0 items-center justify-center", children: icon }),
1728
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: title })
1729
- ]
1730
- }
1731
- );
1732
- }
1733
- function SessionItem({
1734
- session,
1735
- isActive,
1736
- isEditing,
1737
- editTitle,
1738
- setEditTitle,
1739
- renameInputRef,
1740
- onRename,
1741
- onCancelRename,
1742
- onPrefetch,
1743
- onStartRename,
1744
- onTogglePin,
1745
- onCopyId,
1746
- onDelete,
1747
- isPinned = false
1748
- }) {
1749
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMenuItem, { className: "group/chat", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative", children: isEditing ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1750
- "input",
1751
- {
1752
- ref: renameInputRef,
1753
- value: editTitle,
1754
- onChange: (e) => setEditTitle(e.target.value),
1755
- onKeyDown: (e) => {
1756
- if (e.key === "Enter") onRename(session.id);
1757
- if (e.key === "Escape") onCancelRename();
1758
- },
1759
- onBlur: () => onCancelRename(),
1760
- className: "w-full px-2 py-1 text-xs font-medium bg-transparent border-b border-sidebar-ring text-foreground outline-none"
1761
- }
1762
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1763
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1764
- SidebarMenuButton,
1765
- {
1766
- isActive,
1767
- className: "px-2 py-1 pr-8 text-xs font-medium text-muted-foreground hover:text-foreground data-[active=true]:bg-sidebar-muted data-[active=true]:text-foreground",
1768
- onDoubleClick: onStartRename,
1769
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
1770
- Link,
1771
- {
1772
- to: `/chat/${session.id}`,
1773
- className: "flex items-center gap-1.5",
1774
- onPointerEnter: onPrefetch
1775
- }
1776
- ),
1777
- children: [
1778
- isPinned && /* @__PURE__ */ jsxRuntimeExports.jsx(IconPin, { className: "size-3 shrink-0 text-amber-500" }),
1779
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: session.title })
1780
- ]
1781
- }
1782
- ),
1783
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { children: [
1784
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1785
- DropdownMenuTrigger,
1786
- {
1787
- render: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "absolute right-0 top-1/2 -translate-y-1/2 z-10 flex items-center justify-center rounded-md p-1 text-muted-foreground opacity-0 hover:bg-sidebar-muted hover:text-foreground group-hover/chat:opacity-100 transition-opacity" }),
1788
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(IconDots, { className: "size-4" })
1789
- }
1790
- ),
1791
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuContent, { align: "end", side: "right", sideOffset: 4, children: [
1792
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuItem, { className: "gap-2", onClick: onStartRename, children: [
1793
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconPencil, { className: "size-3.5" }),
1794
- "Rename"
1795
- ] }),
1796
- /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { className: "gap-2", onClick: onTogglePin, children: isPinned ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1797
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconPinnedOff, { className: "size-3.5" }),
1798
- "Unpin"
1799
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1800
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconPin, { className: "size-3.5" }),
1801
- "Pin"
1802
- ] }) }),
1803
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuItem, { className: "gap-2", onClick: onCopyId, children: [
1804
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconCopy, { className: "size-3.5" }),
1805
- "Copy ID"
1806
- ] }),
1807
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1808
- DropdownMenuItem,
1809
- {
1810
- className: "gap-2 text-destructive",
1811
- onClick: onDelete,
1812
- children: [
1813
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconTrash, { className: "size-3.5" }),
1814
- "Delete"
1815
- ]
1816
- }
1817
- )
1818
- ] })
1819
- ] })
1820
- ] }) }) });
1821
- }
1822
- function UpdateBanner({ isCollapsed }) {
1823
- const { data, isLoading } = useVersionCheck();
1824
- const updateMutation = useTriggerUpdate();
1825
- const [showPanel, setShowPanel] = reactExports.useState(false);
1826
- if (isLoading || !data?.outdated) return null;
1827
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
1828
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1829
- "button",
1830
- {
1831
- onClick: () => setShowPanel((s) => !s),
1832
- className: cn(
1833
- "flex items-center gap-1.5 rounded-md text-xs font-medium transition-colors w-full",
1834
- isCollapsed ? "justify-center p-1.5 bg-amber-500/10 text-amber-600 hover:bg-amber-500/20 dark:text-amber-400" : "px-2 py-1.5 bg-amber-500/10 text-amber-600 hover:bg-amber-500/20 dark:text-amber-400"
1835
- ),
1836
- title: "Update available",
1837
- children: [
1838
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconDownload, { className: "size-3.5 shrink-0" }),
1839
- !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
1840
- "Update v",
1841
- data.latest
1842
- ] })
1843
- ]
1844
- }
1845
- ),
1846
- showPanel && !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-full left-0 right-0 mb-1.5 rounded-lg border border-border bg-popover p-3 shadow-lg z-50", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1847
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs font-medium text-foreground", children: [
1848
- "Hermium v",
1849
- data.latest,
1850
- " is available"
1851
- ] }),
1852
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-muted-foreground", children: [
1853
- "You are running v",
1854
- data.current,
1855
- "."
1856
- ] }),
1857
- updateMutation.isSuccess && updateMutation.data.success ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md bg-green-500/10 px-2 py-1.5 text-[11px] text-green-600 dark:text-green-400", children: updateMutation.data.message }) : updateMutation.isSuccess && !updateMutation.data.success ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1.5", children: [
1858
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md bg-red-500/10 px-2 py-1.5 text-[11px] text-red-600 dark:text-red-400", children: updateMutation.data.message }),
1859
- updateMutation.data.command && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 rounded-md border border-border bg-muted px-2 py-1.5", children: [
1860
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "flex-1 text-[11px] font-mono text-foreground truncate", children: updateMutation.data.command }),
1861
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1862
- "button",
1863
- {
1864
- onClick: () => navigator.clipboard.writeText(updateMutation.data.command),
1865
- className: "shrink-0 rounded p-0.5 text-muted-foreground hover:text-foreground hover:bg-muted-foreground/10 transition-colors",
1866
- title: "Copy command",
1867
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(IconCopy, { className: "size-3.5" })
1868
- }
1869
- )
1870
- ] })
1871
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5", children: [
1872
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1873
- "button",
1874
- {
1875
- onClick: () => updateMutation.mutate(),
1876
- disabled: updateMutation.isPending,
1877
- className: "flex-1 rounded-md bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50",
1878
- children: updateMutation.isPending ? "Updating..." : "Update now"
1879
- }
1880
- ),
1881
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1882
- "button",
1883
- {
1884
- onClick: () => {
1885
- if (data.updateCommand) {
1886
- navigator.clipboard.writeText(data.updateCommand);
1887
- }
1888
- },
1889
- className: "rounded-md border border-border px-2 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
1890
- children: "Copy cmd"
1891
- }
1892
- )
1893
- ] }),
1894
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1895
- "button",
1896
- {
1897
- onClick: () => setShowPanel(false),
1898
- className: "w-full rounded-md border border-border px-2 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
1899
- children: "Dismiss"
1900
- }
1901
- )
1902
- ] }) })
1903
- ] });
1904
- }
1905
- function DashboardLayout({ children }) {
1906
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarProvider, { style: { "--sidebar-width-icon": "2.5rem" }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex h-dvh w-full", children: [
1907
- /* @__PURE__ */ jsxRuntimeExports.jsx(DashboardSidebar, {}),
1908
- /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarInset, { className: "flex flex-col min-w-0", children })
1909
- ] }) });
1910
- }
1911
- const appCss = "/assets/styles-KcflDlA_.css";
1912
- const themeScript = `
1913
- (function() {
1914
- try {
1915
- var theme = localStorage.getItem('hermium-theme') || 'system';
1916
- var resolved = theme === 'system'
1917
- ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
1918
- : theme;
1919
- if (resolved === 'dark') {
1920
- document.documentElement.classList.add('dark');
1921
- } else {
1922
- document.documentElement.classList.remove('dark');
1923
- }
1924
- } catch (e) {}
1925
- })();
1926
- `;
1927
- const Route$9 = createRootRoute({
1928
- head: () => ({
1929
- meta: [
1930
- { charSet: "utf-8" },
1931
- {
1932
- name: "viewport",
1933
- content: "width=device-width, initial-scale=1"
1934
- },
1935
- { title: "Hermium" }
1936
- ],
1937
- links: [
1938
- { rel: "stylesheet", href: appCss },
1939
- { rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
1940
- { rel: "icon", type: "image/png", href: "/favicon.png" }
1941
- ],
1942
- scripts: [{ type: "text/javascript", children: themeScript }]
1943
- }),
1944
- notFoundComponent: () => /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "container mx-auto p-4 pt-16", children: [
1945
- /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { children: "404" }),
1946
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "The requested page could not be found." })
1947
- ] }),
1948
- shellComponent: RootDocument
1949
- });
1950
- function RootDocument({ children }) {
1951
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("html", { lang: "en", suppressHydrationWarning: true, children: [
1952
- /* @__PURE__ */ jsxRuntimeExports.jsx("head", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(HeadContent, {}) }),
1953
- /* @__PURE__ */ jsxRuntimeExports.jsxs("body", { children: [
1954
- /* @__PURE__ */ jsxRuntimeExports.jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DashboardLayout, { children }) }) }) }),
1955
- false,
1956
- /* @__PURE__ */ jsxRuntimeExports.jsx(Scripts, {})
1957
- ] })
1958
- ] });
1959
- }
1960
- const $$splitComponentImporter$8 = () => import("./index-6itDALOw.mjs");
1961
- const Route$8 = createFileRoute("/")({
1962
- component: lazyRouteComponent($$splitComponentImporter$8, "component")
1963
- });
1964
- const $$splitComponentImporter$7 = () => import("./index-C_ZxnypN.mjs");
1965
- const Route$7 = createFileRoute("/usage/")({
1966
- component: lazyRouteComponent($$splitComponentImporter$7, "component")
1967
- });
1968
- const $$splitComponentImporter$6 = () => import("./index-BIRTrOmp.mjs");
1969
- const Route$6 = createFileRoute("/skills/")({
1970
- component: lazyRouteComponent($$splitComponentImporter$6, "component")
1971
- });
1972
- const $$splitComponentImporter$5 = () => import("./index-C5HpvlUP.mjs");
1973
- const settingsSearchSchema = object({
1974
- section: string().optional().default("appearance")
1975
- });
1976
- const Route$5 = createFileRoute("/settings/")({
1977
- component: lazyRouteComponent($$splitComponentImporter$5, "component"),
1978
- validateSearch: (search) => settingsSearchSchema.parse(search)
1979
- });
1980
- const $$splitComponentImporter$4 = () => import("./index-0n2Z3BPQ.mjs");
1981
- const Route$4 = createFileRoute("/memory/")({
1982
- component: lazyRouteComponent($$splitComponentImporter$4, "component")
1983
- });
1984
- const $$splitComponentImporter$3 = () => import("./index-BPzfADac.mjs");
1985
- const Route$3 = createFileRoute("/chat/")({
1986
- component: lazyRouteComponent($$splitComponentImporter$3, "component")
1987
- });
1988
- const $$splitComponentImporter$2 = () => import("./index-DNVESZiA.mjs");
1989
- const Route$2 = createFileRoute("/channels/")({
1990
- component: lazyRouteComponent($$splitComponentImporter$2, "component")
1991
- });
1992
- const $$splitComponentImporter$1 = () => import("./index-Ca8JFH8f.mjs");
1993
- const Route$1 = createFileRoute("/automations/")({
1994
- component: lazyRouteComponent($$splitComponentImporter$1, "component")
1995
- });
1996
- const $$splitComponentImporter = () => import("./index-BQE3bF14.mjs");
1997
- const Route = createFileRoute("/chat/$sessionId/")({
1998
- component: lazyRouteComponent($$splitComponentImporter, "component")
1999
- });
2000
- const IndexRoute = Route$8.update({
2001
- id: "/",
2002
- path: "/",
2003
- getParentRoute: () => Route$9
2004
- });
2005
- const UsageIndexRoute = Route$7.update({
2006
- id: "/usage/",
2007
- path: "/usage/",
2008
- getParentRoute: () => Route$9
2009
- });
2010
- const SkillsIndexRoute = Route$6.update({
2011
- id: "/skills/",
2012
- path: "/skills/",
2013
- getParentRoute: () => Route$9
2014
- });
2015
- const SettingsIndexRoute = Route$5.update({
2016
- id: "/settings/",
2017
- path: "/settings/",
2018
- getParentRoute: () => Route$9
2019
- });
2020
- const MemoryIndexRoute = Route$4.update({
2021
- id: "/memory/",
2022
- path: "/memory/",
2023
- getParentRoute: () => Route$9
2024
- });
2025
- const ChatIndexRoute = Route$3.update({
2026
- id: "/chat/",
2027
- path: "/chat/",
2028
- getParentRoute: () => Route$9
2029
- });
2030
- const ChannelsIndexRoute = Route$2.update({
2031
- id: "/channels/",
2032
- path: "/channels/",
2033
- getParentRoute: () => Route$9
2034
- });
2035
- const AutomationsIndexRoute = Route$1.update({
2036
- id: "/automations/",
2037
- path: "/automations/",
2038
- getParentRoute: () => Route$9
2039
- });
2040
- const ChatSessionIdIndexRoute = Route.update({
2041
- id: "/chat/$sessionId/",
2042
- path: "/chat/$sessionId/",
2043
- getParentRoute: () => Route$9
2044
- });
2045
- const rootRouteChildren = {
2046
- IndexRoute,
2047
- AutomationsIndexRoute,
2048
- ChannelsIndexRoute,
2049
- ChatIndexRoute,
2050
- MemoryIndexRoute,
2051
- SettingsIndexRoute,
2052
- SkillsIndexRoute,
2053
- UsageIndexRoute,
2054
- ChatSessionIdIndexRoute
2055
- };
2056
- const routeTree = Route$9._addFileChildren(rootRouteChildren)._addFileTypes();
2057
- function getRouter() {
2058
- const router2 = createRouter({
2059
- routeTree,
2060
- scrollRestoration: true,
2061
- defaultPreload: "intent",
2062
- defaultPreloadStaleTime: 0
2063
- });
2064
- return router2;
2065
- }
2066
- const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2067
- __proto__: null,
2068
- getRouter
2069
- }, Symbol.toStringTag, { value: "Module" }));
2070
- export {
2071
- Button as B,
2072
- DropdownMenu as D,
2073
- Route$5 as R,
2074
- cn as a,
2075
- DropdownMenuTrigger as b,
2076
- createSession as c,
2077
- DropdownMenuContent as d,
2078
- DropdownMenuItem as e,
2079
- useTheme as f,
2080
- get as g,
2081
- useChatStore as h,
2082
- put as i,
2083
- patch as j,
2084
- del as k,
2085
- deleteSessionApi as l,
2086
- respondApproval as m,
2087
- Route as n,
2088
- fetchModels as o,
2089
- post as p,
2090
- router as q,
2091
- renameSession as r,
2092
- updateModelConfig as u
2093
- };