hermium 0.1.10 → 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 (212) 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-BW147gsG.js +0 -1
  28. package/dist/public/assets/IconAlertTriangle-DCoTLVSd.js +0 -1
  29. package/dist/public/assets/IconCheck-DAO7Fpl9.js +0 -1
  30. package/dist/public/assets/IconCode-BqfTl5wU.js +0 -1
  31. package/dist/public/assets/IconLoader2-B_pehSXN.js +0 -1
  32. package/dist/public/assets/IconRefresh-BbMGoMV8.js +0 -1
  33. package/dist/public/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  34. package/dist/public/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  35. package/dist/public/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  36. package/dist/public/assets/index-BL9a2Xg9.js +0 -1
  37. package/dist/public/assets/index-BSwXjgjr.js +0 -1
  38. package/dist/public/assets/index-BWl8tn18.js +0 -1
  39. package/dist/public/assets/index-CQh8SXb2.js +0 -94
  40. package/dist/public/assets/index-Cxd-kSUY.js +0 -1
  41. package/dist/public/assets/index-DA5SH--7.js +0 -2
  42. package/dist/public/assets/index-DCHbvtBS.js +0 -1
  43. package/dist/public/assets/index-DCYXJZEe.js +0 -1
  44. package/dist/public/assets/index-DFDfp0ca.js +0 -1
  45. package/dist/public/assets/index-GuAAqSCJ.js +0 -14
  46. package/dist/public/assets/index-WIDirTHx.js +0 -29
  47. package/dist/public/assets/index-X3XZcAzy.js +0 -1
  48. package/dist/public/assets/input-7TQEEJq6.js +0 -1
  49. package/dist/public/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  50. package/dist/public/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  51. package/dist/public/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  52. package/dist/public/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  53. package/dist/public/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  54. package/dist/public/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  55. package/dist/public/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  56. package/dist/public/assets/models-BlUb1eaf.js +0 -1
  57. package/dist/public/assets/styles-PHtUFHUr.css +0 -1
  58. package/dist/public/assets/switch-B_iYUUM3.js +0 -1
  59. package/dist/public/assets/syntax-highlighter-DcBYfnEK.js +0 -6
  60. package/dist/public/assets/textarea-Dm4aaRqS.js +0 -1
  61. package/dist/public/favicon.png +0 -0
  62. package/dist/public/nous-logo.png +0 -0
  63. package/dist/server/index.mjs +0 -244
  64. package/dist/web-server/__23tanstack-start-plugin-adapters-Cwee5PKy.mjs +0 -6
  65. package/dist/web-server/_chunks/ssr-renderer.mjs +0 -22
  66. package/dist/web-server/_libs/babel__runtime.mjs +0 -237
  67. package/dist/web-server/_libs/bail.mjs +0 -8
  68. package/dist/web-server/_libs/base-ui__react.mjs +0 -9554
  69. package/dist/web-server/_libs/base-ui__utils.mjs +0 -1101
  70. package/dist/web-server/_libs/ccount.mjs +0 -16
  71. package/dist/web-server/_libs/character-entities-legacy.mjs +0 -111
  72. package/dist/web-server/_libs/character-entities.mjs +0 -2130
  73. package/dist/web-server/_libs/character-reference-invalid.mjs +0 -33
  74. package/dist/web-server/_libs/class-variance-authority.mjs +0 -44
  75. package/dist/web-server/_libs/clsx.mjs +0 -16
  76. package/dist/web-server/_libs/comma-separated-tokens.mjs +0 -31
  77. package/dist/web-server/_libs/cookie-es.mjs +0 -44
  78. package/dist/web-server/_libs/croner.mjs +0 -1
  79. package/dist/web-server/_libs/crossws.mjs +0 -1
  80. package/dist/web-server/_libs/decode-named-character-reference+[...].mjs +0 -8
  81. package/dist/web-server/_libs/devlop.mjs +0 -8
  82. package/dist/web-server/_libs/escape-string-regexp.mjs +0 -9
  83. package/dist/web-server/_libs/estree-util-is-identifier-name.mjs +0 -11
  84. package/dist/web-server/_libs/extend.mjs +0 -97
  85. package/dist/web-server/_libs/fault.mjs +0 -1
  86. package/dist/web-server/_libs/floating-ui__core.mjs +0 -663
  87. package/dist/web-server/_libs/floating-ui__dom.mjs +0 -624
  88. package/dist/web-server/_libs/floating-ui__react-dom.mjs +0 -279
  89. package/dist/web-server/_libs/floating-ui__utils.mjs +0 -322
  90. package/dist/web-server/_libs/format.mjs +0 -1
  91. package/dist/web-server/_libs/h3.mjs +0 -408
  92. package/dist/web-server/_libs/hast-util-parse-selector.mjs +0 -39
  93. package/dist/web-server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
  94. package/dist/web-server/_libs/hast-util-whitespace.mjs +0 -10
  95. package/dist/web-server/_libs/hastscript.mjs +0 -200
  96. package/dist/web-server/_libs/highlight.js.mjs +0 -1
  97. package/dist/web-server/_libs/hookable.mjs +0 -1
  98. package/dist/web-server/_libs/html-url-attributes.mjs +0 -26
  99. package/dist/web-server/_libs/inline-style-parser.mjs +0 -142
  100. package/dist/web-server/_libs/is-alphabetical.mjs +0 -7
  101. package/dist/web-server/_libs/is-alphanumerical.mjs +0 -8
  102. package/dist/web-server/_libs/is-decimal.mjs +0 -7
  103. package/dist/web-server/_libs/is-hexadecimal.mjs +0 -7
  104. package/dist/web-server/_libs/is-plain-obj.mjs +0 -10
  105. package/dist/web-server/_libs/isbot.mjs +0 -21
  106. package/dist/web-server/_libs/longest-streak.mjs +0 -25
  107. package/dist/web-server/_libs/lowlight.mjs +0 -1
  108. package/dist/web-server/_libs/markdown-table.mjs +0 -142
  109. package/dist/web-server/_libs/mdast-util-find-and-replace.mjs +0 -109
  110. package/dist/web-server/_libs/mdast-util-from-markdown.mjs +0 -717
  111. package/dist/web-server/_libs/mdast-util-gfm-autolink-literal+[...].mjs +0 -156
  112. package/dist/web-server/_libs/mdast-util-gfm-footnote.mjs +0 -117
  113. package/dist/web-server/_libs/mdast-util-gfm-strikethrough.mjs +0 -54
  114. package/dist/web-server/_libs/mdast-util-gfm-table.mjs +0 -157
  115. package/dist/web-server/_libs/mdast-util-gfm-task-list-item.mjs +0 -77
  116. package/dist/web-server/_libs/mdast-util-gfm.mjs +0 -29
  117. package/dist/web-server/_libs/mdast-util-phrasing.mjs +0 -30
  118. package/dist/web-server/_libs/mdast-util-to-hast.mjs +0 -710
  119. package/dist/web-server/_libs/mdast-util-to-markdown.mjs +0 -798
  120. package/dist/web-server/_libs/mdast-util-to-string.mjs +0 -38
  121. package/dist/web-server/_libs/micromark-core-commonmark.mjs +0 -2259
  122. package/dist/web-server/_libs/micromark-extension-gfm-autolink-literal+[...].mjs +0 -344
  123. package/dist/web-server/_libs/micromark-extension-gfm-footnote+[...].mjs +0 -279
  124. package/dist/web-server/_libs/micromark-extension-gfm-strikethrough+[...].mjs +0 -98
  125. package/dist/web-server/_libs/micromark-extension-gfm-table.mjs +0 -491
  126. package/dist/web-server/_libs/micromark-extension-gfm-tagfilter+[...].mjs +0 -1
  127. package/dist/web-server/_libs/micromark-extension-gfm-task-list-item+[...].mjs +0 -77
  128. package/dist/web-server/_libs/micromark-extension-gfm.mjs +0 -18
  129. package/dist/web-server/_libs/micromark-factory-destination.mjs +0 -94
  130. package/dist/web-server/_libs/micromark-factory-label.mjs +0 -63
  131. package/dist/web-server/_libs/micromark-factory-space.mjs +0 -24
  132. package/dist/web-server/_libs/micromark-factory-title.mjs +0 -65
  133. package/dist/web-server/_libs/micromark-factory-whitespace.mjs +0 -22
  134. package/dist/web-server/_libs/micromark-util-character.mjs +0 -44
  135. package/dist/web-server/_libs/micromark-util-chunked.mjs +0 -36
  136. package/dist/web-server/_libs/micromark-util-classify-character+[...].mjs +0 -12
  137. package/dist/web-server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
  138. package/dist/web-server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
  139. package/dist/web-server/_libs/micromark-util-decode-string.mjs +0 -21
  140. package/dist/web-server/_libs/micromark-util-encode.mjs +0 -1
  141. package/dist/web-server/_libs/micromark-util-html-tag-name.mjs +0 -69
  142. package/dist/web-server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
  143. package/dist/web-server/_libs/micromark-util-resolve-all.mjs +0 -15
  144. package/dist/web-server/_libs/micromark-util-sanitize-uri.mjs +0 -41
  145. package/dist/web-server/_libs/micromark-util-subtokenize.mjs +0 -346
  146. package/dist/web-server/_libs/micromark.mjs +0 -906
  147. package/dist/web-server/_libs/ocache.mjs +0 -1
  148. package/dist/web-server/_libs/ohash.mjs +0 -1
  149. package/dist/web-server/_libs/parse-entities.mjs +0 -245
  150. package/dist/web-server/_libs/property-information.mjs +0 -1210
  151. package/dist/web-server/_libs/react-dom.mjs +0 -10779
  152. package/dist/web-server/_libs/react-markdown.mjs +0 -147
  153. package/dist/web-server/_libs/react-syntax-highlighter.mjs +0 -941
  154. package/dist/web-server/_libs/react.mjs +0 -513
  155. package/dist/web-server/_libs/refractor.mjs +0 -2425
  156. package/dist/web-server/_libs/remark-gfm.mjs +0 -20
  157. package/dist/web-server/_libs/remark-parse.mjs +0 -19
  158. package/dist/web-server/_libs/remark-rehype.mjs +0 -21
  159. package/dist/web-server/_libs/reselect.mjs +0 -1
  160. package/dist/web-server/_libs/rou3.mjs +0 -8
  161. package/dist/web-server/_libs/seroval-plugins.mjs +0 -58
  162. package/dist/web-server/_libs/seroval.mjs +0 -1775
  163. package/dist/web-server/_libs/space-separated-tokens.mjs +0 -11
  164. package/dist/web-server/_libs/srvx.mjs +0 -781
  165. package/dist/web-server/_libs/style-to-js.mjs +0 -72
  166. package/dist/web-server/_libs/style-to-object.mjs +0 -38
  167. package/dist/web-server/_libs/tabler__icons-react.mjs +0 -233
  168. package/dist/web-server/_libs/tanstack__history.mjs +0 -204
  169. package/dist/web-server/_libs/tanstack__query-core.mjs +0 -2552
  170. package/dist/web-server/_libs/tanstack__react-query.mjs +0 -190
  171. package/dist/web-server/_libs/tanstack__react-router.mjs +0 -1120
  172. package/dist/web-server/_libs/tanstack__react-store.mjs +0 -2
  173. package/dist/web-server/_libs/tanstack__router-core.mjs +0 -4288
  174. package/dist/web-server/_libs/tanstack__store.mjs +0 -1
  175. package/dist/web-server/_libs/trim-lines.mjs +0 -41
  176. package/dist/web-server/_libs/trough.mjs +0 -85
  177. package/dist/web-server/_libs/ufo.mjs +0 -54
  178. package/dist/web-server/_libs/unctx.mjs +0 -1
  179. package/dist/web-server/_libs/ungap__structured-clone.mjs +0 -224
  180. package/dist/web-server/_libs/unified.mjs +0 -661
  181. package/dist/web-server/_libs/unist-util-is.mjs +0 -100
  182. package/dist/web-server/_libs/unist-util-position.mjs +0 -27
  183. package/dist/web-server/_libs/unist-util-stringify-position.mjs +0 -27
  184. package/dist/web-server/_libs/unist-util-visit-parents.mjs +0 -83
  185. package/dist/web-server/_libs/unist-util-visit.mjs +0 -24
  186. package/dist/web-server/_libs/unstorage.mjs +0 -1
  187. package/dist/web-server/_libs/use-sync-external-store.mjs +0 -139
  188. package/dist/web-server/_libs/vfile-message.mjs +0 -138
  189. package/dist/web-server/_libs/vfile.mjs +0 -467
  190. package/dist/web-server/_libs/zod.mjs +0 -3915
  191. package/dist/web-server/_libs/zustand.mjs +0 -343
  192. package/dist/web-server/_libs/zwitch.mjs +0 -1
  193. package/dist/web-server/_ssr/index--Bo2_ipW.mjs +0 -277
  194. package/dist/web-server/_ssr/index-BEvygh5x.mjs +0 -612
  195. package/dist/web-server/_ssr/index-BFaKxYfN.mjs +0 -40
  196. package/dist/web-server/_ssr/index-BdrMzRTd.mjs +0 -513
  197. package/dist/web-server/_ssr/index-CoDfv1vI.mjs +0 -66
  198. package/dist/web-server/_ssr/index-Cwp8Gyl1.mjs +0 -1812
  199. package/dist/web-server/_ssr/index-DQsfKYjV.mjs +0 -449
  200. package/dist/web-server/_ssr/index-Dbs9k8Ea.mjs +0 -251
  201. package/dist/web-server/_ssr/index-Dk93oyZD.mjs +0 -213
  202. package/dist/web-server/_ssr/index-KNJ7huw_.mjs +0 -369
  203. package/dist/web-server/_ssr/index.mjs +0 -1558
  204. package/dist/web-server/_ssr/input-BV1DMASc.mjs +0 -20
  205. package/dist/web-server/_ssr/models-MzrvbL2i.mjs +0 -43
  206. package/dist/web-server/_ssr/router-CS6Zq3md.mjs +0 -2134
  207. package/dist/web-server/_ssr/start-HYkvq4Ni.mjs +0 -4
  208. package/dist/web-server/_ssr/switch-Bd-Sg0HG.mjs +0 -33
  209. package/dist/web-server/_ssr/syntax-highlighter-5vezNTce.mjs +0 -62
  210. package/dist/web-server/_ssr/textarea-CB4kQp9w.mjs +0 -18
  211. package/dist/web-server/_tanstack-start-manifest_v-m5lY48LR.mjs +0 -4
  212. package/dist/web-server/index.mjs +0 -611
@@ -1,2134 +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 IconBrain, g as IconBrandGithub, h as IconArrowLeft, i as IconLayoutSidebar, j as IconChevronUp, k as IconChevronDown, l as IconCirclePlus, 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
- SettingsLink,
1684
- {
1685
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconBrain, { className: "size-4" }),
1686
- title: "Models",
1687
- to: "/settings/models"
1688
- }
1689
- ),
1690
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1691
- SettingsGroup,
1692
- {
1693
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(IconBrandGithub, { className: "size-4" }),
1694
- title: "About",
1695
- section: "about",
1696
- activeSection: currentSection
1697
- }
1698
- )
1699
- ] })
1700
- ] }),
1701
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 px-0.5 pb-1", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1702
- Link,
1703
- {
1704
- to: "/",
1705
- 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",
1706
- children: [
1707
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconArrowLeft, { className: "size-4 shrink-0" }),
1708
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Back to app" })
1709
- ]
1710
- }
1711
- ) })
1712
- ] })
1713
- ]
1714
- }
1715
- ) }) }) })
1716
- ] });
1717
- }
1718
- function SettingsGroup({
1719
- icon,
1720
- title,
1721
- section,
1722
- activeSection
1723
- }) {
1724
- const isActive = activeSection === section;
1725
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1726
- Link,
1727
- {
1728
- to: "/settings",
1729
- search: { section },
1730
- className: cn(
1731
- "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors",
1732
- isActive ? "bg-sidebar-muted text-foreground font-medium" : "text-muted-foreground hover:bg-sidebar-muted hover:text-foreground"
1733
- ),
1734
- children: [
1735
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex shrink-0 items-center justify-center", children: icon }),
1736
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: title })
1737
- ]
1738
- }
1739
- );
1740
- }
1741
- function SettingsLink({
1742
- icon,
1743
- title,
1744
- to
1745
- }) {
1746
- const location = useLocation();
1747
- const isActive = location.pathname === to;
1748
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1749
- Link,
1750
- {
1751
- to,
1752
- className: cn(
1753
- "flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors",
1754
- isActive ? "bg-sidebar-muted text-foreground font-medium" : "text-muted-foreground hover:bg-sidebar-muted hover:text-foreground"
1755
- ),
1756
- children: [
1757
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex shrink-0 items-center justify-center", children: icon }),
1758
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: title })
1759
- ]
1760
- }
1761
- );
1762
- }
1763
- function SessionItem({
1764
- session,
1765
- isActive,
1766
- isEditing,
1767
- editTitle,
1768
- setEditTitle,
1769
- renameInputRef,
1770
- onRename,
1771
- onCancelRename,
1772
- onPrefetch,
1773
- onStartRename,
1774
- onTogglePin,
1775
- onCopyId,
1776
- onDelete,
1777
- isPinned = false
1778
- }) {
1779
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMenuItem, { className: "group/chat", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative", children: isEditing ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1780
- "input",
1781
- {
1782
- ref: renameInputRef,
1783
- value: editTitle,
1784
- onChange: (e) => setEditTitle(e.target.value),
1785
- onKeyDown: (e) => {
1786
- if (e.key === "Enter") onRename(session.id);
1787
- if (e.key === "Escape") onCancelRename();
1788
- },
1789
- onBlur: () => onCancelRename(),
1790
- className: "w-full px-2 py-1 text-xs font-medium bg-transparent border-b border-sidebar-ring text-foreground outline-none"
1791
- }
1792
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1793
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1794
- SidebarMenuButton,
1795
- {
1796
- isActive,
1797
- 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",
1798
- onDoubleClick: onStartRename,
1799
- render: /* @__PURE__ */ jsxRuntimeExports.jsx(
1800
- Link,
1801
- {
1802
- to: `/chat/${session.id}`,
1803
- className: "flex items-center gap-1.5",
1804
- onPointerEnter: onPrefetch
1805
- }
1806
- ),
1807
- children: [
1808
- isPinned && /* @__PURE__ */ jsxRuntimeExports.jsx(IconPin, { className: "size-3 shrink-0 text-amber-500" }),
1809
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: session.title })
1810
- ]
1811
- }
1812
- ),
1813
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenu, { children: [
1814
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1815
- DropdownMenuTrigger,
1816
- {
1817
- 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" }),
1818
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(IconDots, { className: "size-4" })
1819
- }
1820
- ),
1821
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuContent, { align: "end", side: "right", sideOffset: 4, children: [
1822
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuItem, { className: "gap-2", onClick: onStartRename, children: [
1823
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconPencil, { className: "size-3.5" }),
1824
- "Rename"
1825
- ] }),
1826
- /* @__PURE__ */ jsxRuntimeExports.jsx(DropdownMenuItem, { className: "gap-2", onClick: onTogglePin, children: isPinned ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1827
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconPinnedOff, { className: "size-3.5" }),
1828
- "Unpin"
1829
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1830
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconPin, { className: "size-3.5" }),
1831
- "Pin"
1832
- ] }) }),
1833
- /* @__PURE__ */ jsxRuntimeExports.jsxs(DropdownMenuItem, { className: "gap-2", onClick: onCopyId, children: [
1834
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconCopy, { className: "size-3.5" }),
1835
- "Copy ID"
1836
- ] }),
1837
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1838
- DropdownMenuItem,
1839
- {
1840
- className: "gap-2 text-destructive",
1841
- onClick: onDelete,
1842
- children: [
1843
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconTrash, { className: "size-3.5" }),
1844
- "Delete"
1845
- ]
1846
- }
1847
- )
1848
- ] })
1849
- ] })
1850
- ] }) }) });
1851
- }
1852
- function UpdateBanner({ isCollapsed }) {
1853
- const { data, isLoading } = useVersionCheck();
1854
- const updateMutation = useTriggerUpdate();
1855
- const [showPanel, setShowPanel] = reactExports.useState(false);
1856
- if (isLoading || !data?.outdated) return null;
1857
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
1858
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
1859
- "button",
1860
- {
1861
- onClick: () => setShowPanel((s) => !s),
1862
- className: cn(
1863
- "flex items-center gap-1.5 rounded-md text-xs font-medium transition-colors w-full",
1864
- 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"
1865
- ),
1866
- title: "Update available",
1867
- children: [
1868
- /* @__PURE__ */ jsxRuntimeExports.jsx(IconDownload, { className: "size-3.5 shrink-0" }),
1869
- !isCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
1870
- "Update v",
1871
- data.latest
1872
- ] })
1873
- ]
1874
- }
1875
- ),
1876
- 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: [
1877
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs font-medium text-foreground", children: [
1878
- "Hermium v",
1879
- data.latest,
1880
- " is available"
1881
- ] }),
1882
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[11px] text-muted-foreground", children: [
1883
- "You are running v",
1884
- data.current,
1885
- "."
1886
- ] }),
1887
- 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: [
1888
- /* @__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 }),
1889
- 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: [
1890
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "flex-1 text-[11px] font-mono text-foreground truncate", children: updateMutation.data.command }),
1891
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1892
- "button",
1893
- {
1894
- onClick: () => navigator.clipboard.writeText(updateMutation.data.command),
1895
- className: "shrink-0 rounded p-0.5 text-muted-foreground hover:text-foreground hover:bg-muted-foreground/10 transition-colors",
1896
- title: "Copy command",
1897
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(IconCopy, { className: "size-3.5" })
1898
- }
1899
- )
1900
- ] })
1901
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5", children: [
1902
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1903
- "button",
1904
- {
1905
- onClick: () => updateMutation.mutate(),
1906
- disabled: updateMutation.isPending,
1907
- 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",
1908
- children: updateMutation.isPending ? "Updating..." : "Update now"
1909
- }
1910
- ),
1911
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1912
- "button",
1913
- {
1914
- onClick: () => {
1915
- if (data.updateCommand) {
1916
- navigator.clipboard.writeText(data.updateCommand);
1917
- }
1918
- },
1919
- 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",
1920
- children: "Copy cmd"
1921
- }
1922
- )
1923
- ] }),
1924
- /* @__PURE__ */ jsxRuntimeExports.jsx(
1925
- "button",
1926
- {
1927
- onClick: () => setShowPanel(false),
1928
- 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",
1929
- children: "Dismiss"
1930
- }
1931
- )
1932
- ] }) })
1933
- ] });
1934
- }
1935
- function DashboardLayout({ children }) {
1936
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarProvider, { style: { "--sidebar-width-icon": "2.5rem" }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex h-dvh w-full", children: [
1937
- /* @__PURE__ */ jsxRuntimeExports.jsx(DashboardSidebar, {}),
1938
- /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarInset, { className: "flex flex-col min-w-0", children })
1939
- ] }) });
1940
- }
1941
- const appCss = "/assets/styles-PHtUFHUr.css";
1942
- const themeScript = `
1943
- (function() {
1944
- try {
1945
- var theme = localStorage.getItem('hermium-theme') || 'system';
1946
- var resolved = theme === 'system'
1947
- ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
1948
- : theme;
1949
- if (resolved === 'dark') {
1950
- document.documentElement.classList.add('dark');
1951
- } else {
1952
- document.documentElement.classList.remove('dark');
1953
- }
1954
- } catch (e) {}
1955
- })();
1956
- `;
1957
- const Route$a = createRootRoute({
1958
- head: () => ({
1959
- meta: [
1960
- { charSet: "utf-8" },
1961
- {
1962
- name: "viewport",
1963
- content: "width=device-width, initial-scale=1"
1964
- },
1965
- { title: "Hermium" }
1966
- ],
1967
- links: [
1968
- { rel: "stylesheet", href: appCss },
1969
- { rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
1970
- { rel: "icon", type: "image/png", href: "/favicon.png" }
1971
- ],
1972
- scripts: [{ type: "text/javascript", children: themeScript }]
1973
- }),
1974
- notFoundComponent: () => /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "container mx-auto p-4 pt-16", children: [
1975
- /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { children: "404" }),
1976
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "The requested page could not be found." })
1977
- ] }),
1978
- shellComponent: RootDocument
1979
- });
1980
- function RootDocument({ children }) {
1981
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("html", { lang: "en", suppressHydrationWarning: true, children: [
1982
- /* @__PURE__ */ jsxRuntimeExports.jsx("head", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(HeadContent, {}) }),
1983
- /* @__PURE__ */ jsxRuntimeExports.jsxs("body", { children: [
1984
- /* @__PURE__ */ jsxRuntimeExports.jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DashboardLayout, { children }) }) }) }),
1985
- false,
1986
- /* @__PURE__ */ jsxRuntimeExports.jsx(Scripts, {})
1987
- ] })
1988
- ] });
1989
- }
1990
- const $$splitComponentImporter$9 = () => import("./index--Bo2_ipW.mjs");
1991
- const Route$9 = createFileRoute("/")({
1992
- component: lazyRouteComponent($$splitComponentImporter$9, "component")
1993
- });
1994
- const $$splitComponentImporter$8 = () => import("./index-Dk93oyZD.mjs");
1995
- const Route$8 = createFileRoute("/usage/")({
1996
- component: lazyRouteComponent($$splitComponentImporter$8, "component")
1997
- });
1998
- const $$splitComponentImporter$7 = () => import("./index-DQsfKYjV.mjs");
1999
- const Route$7 = createFileRoute("/skills/")({
2000
- component: lazyRouteComponent($$splitComponentImporter$7, "component")
2001
- });
2002
- const $$splitComponentImporter$6 = () => import("./index-BFaKxYfN.mjs");
2003
- const settingsSearchSchema = object({
2004
- section: string().optional().default("appearance")
2005
- });
2006
- const Route$6 = createFileRoute("/settings/")({
2007
- component: lazyRouteComponent($$splitComponentImporter$6, "component"),
2008
- validateSearch: (search) => settingsSearchSchema.parse(search)
2009
- });
2010
- const $$splitComponentImporter$5 = () => import("./index-KNJ7huw_.mjs");
2011
- const Route$5 = createFileRoute("/memory/")({
2012
- component: lazyRouteComponent($$splitComponentImporter$5, "component")
2013
- });
2014
- const $$splitComponentImporter$4 = () => import("./index-CoDfv1vI.mjs");
2015
- const Route$4 = createFileRoute("/chat/")({
2016
- component: lazyRouteComponent($$splitComponentImporter$4, "component")
2017
- });
2018
- const $$splitComponentImporter$3 = () => import("./index-BdrMzRTd.mjs");
2019
- const Route$3 = createFileRoute("/channels/")({
2020
- component: lazyRouteComponent($$splitComponentImporter$3, "component")
2021
- });
2022
- const $$splitComponentImporter$2 = () => import("./index-BEvygh5x.mjs");
2023
- const Route$2 = createFileRoute("/automations/")({
2024
- component: lazyRouteComponent($$splitComponentImporter$2, "component")
2025
- });
2026
- const $$splitComponentImporter$1 = () => import("./models-MzrvbL2i.mjs");
2027
- const Route$1 = createFileRoute("/settings/models")({
2028
- component: lazyRouteComponent($$splitComponentImporter$1, "component")
2029
- });
2030
- const $$splitComponentImporter = () => import("./index-Cwp8Gyl1.mjs");
2031
- const Route = createFileRoute("/chat/$sessionId/")({
2032
- component: lazyRouteComponent($$splitComponentImporter, "component")
2033
- });
2034
- const IndexRoute = Route$9.update({
2035
- id: "/",
2036
- path: "/",
2037
- getParentRoute: () => Route$a
2038
- });
2039
- const UsageIndexRoute = Route$8.update({
2040
- id: "/usage/",
2041
- path: "/usage/",
2042
- getParentRoute: () => Route$a
2043
- });
2044
- const SkillsIndexRoute = Route$7.update({
2045
- id: "/skills/",
2046
- path: "/skills/",
2047
- getParentRoute: () => Route$a
2048
- });
2049
- const SettingsIndexRoute = Route$6.update({
2050
- id: "/settings/",
2051
- path: "/settings/",
2052
- getParentRoute: () => Route$a
2053
- });
2054
- const MemoryIndexRoute = Route$5.update({
2055
- id: "/memory/",
2056
- path: "/memory/",
2057
- getParentRoute: () => Route$a
2058
- });
2059
- const ChatIndexRoute = Route$4.update({
2060
- id: "/chat/",
2061
- path: "/chat/",
2062
- getParentRoute: () => Route$a
2063
- });
2064
- const ChannelsIndexRoute = Route$3.update({
2065
- id: "/channels/",
2066
- path: "/channels/",
2067
- getParentRoute: () => Route$a
2068
- });
2069
- const AutomationsIndexRoute = Route$2.update({
2070
- id: "/automations/",
2071
- path: "/automations/",
2072
- getParentRoute: () => Route$a
2073
- });
2074
- const SettingsModelsRoute = Route$1.update({
2075
- id: "/settings/models",
2076
- path: "/settings/models",
2077
- getParentRoute: () => Route$a
2078
- });
2079
- const ChatSessionIdIndexRoute = Route.update({
2080
- id: "/chat/$sessionId/",
2081
- path: "/chat/$sessionId/",
2082
- getParentRoute: () => Route$a
2083
- });
2084
- const rootRouteChildren = {
2085
- IndexRoute,
2086
- SettingsModelsRoute,
2087
- AutomationsIndexRoute,
2088
- ChannelsIndexRoute,
2089
- ChatIndexRoute,
2090
- MemoryIndexRoute,
2091
- SettingsIndexRoute,
2092
- SkillsIndexRoute,
2093
- UsageIndexRoute,
2094
- ChatSessionIdIndexRoute
2095
- };
2096
- const routeTree = Route$a._addFileChildren(rootRouteChildren)._addFileTypes();
2097
- function getRouter() {
2098
- const router2 = createRouter({
2099
- routeTree,
2100
- scrollRestoration: true,
2101
- defaultPreload: "intent",
2102
- defaultPreloadStaleTime: 0
2103
- });
2104
- return router2;
2105
- }
2106
- const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2107
- __proto__: null,
2108
- getRouter
2109
- }, Symbol.toStringTag, { value: "Module" }));
2110
- export {
2111
- Button as B,
2112
- DropdownMenu as D,
2113
- Route$6 as R,
2114
- cn as a,
2115
- put as b,
2116
- createSession as c,
2117
- patch as d,
2118
- del as e,
2119
- fetchModels as f,
2120
- get as g,
2121
- updateModelConfig as h,
2122
- useVersionCheck as i,
2123
- useTheme as j,
2124
- DropdownMenuTrigger as k,
2125
- DropdownMenuContent as l,
2126
- DropdownMenuItem as m,
2127
- deleteSessionApi as n,
2128
- respondApproval as o,
2129
- post as p,
2130
- Route as q,
2131
- renameSession as r,
2132
- router as s,
2133
- useChatStore as u
2134
- };