@harbinger-ai/harbinger 0.1.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 (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/agents/README.md +76 -0
  4. package/agents/_template/CONFIG.yaml +7 -0
  5. package/agents/_template/HEARTBEAT.md +59 -0
  6. package/agents/_template/IDENTITY.md +4 -0
  7. package/agents/_template/SKILLS.md +1 -0
  8. package/agents/_template/SOUL.md +25 -0
  9. package/agents/_template/TOOLS.md +3 -0
  10. package/agents/binary-reverser/CONFIG.yaml +21 -0
  11. package/agents/binary-reverser/HEARTBEAT.md +65 -0
  12. package/agents/binary-reverser/IDENTITY.md +1 -0
  13. package/agents/binary-reverser/SKILLS.md +1 -0
  14. package/agents/binary-reverser/SOUL.md +23 -0
  15. package/agents/binary-reverser/TOOLS.md +99 -0
  16. package/agents/browser-agent/CONFIG.yaml +20 -0
  17. package/agents/browser-agent/HEARTBEAT.md +79 -0
  18. package/agents/browser-agent/IDENTITY.md +5 -0
  19. package/agents/browser-agent/SKILLS.md +86 -0
  20. package/agents/browser-agent/SOUL.md +23 -0
  21. package/agents/browser-agent/TOOLS.md +186 -0
  22. package/agents/cloud-infiltrator/CONFIG.yaml +22 -0
  23. package/agents/cloud-infiltrator/HEARTBEAT.md +78 -0
  24. package/agents/cloud-infiltrator/IDENTITY.md +1 -0
  25. package/agents/cloud-infiltrator/SKILLS.md +1 -0
  26. package/agents/cloud-infiltrator/SOUL.md +23 -0
  27. package/agents/cloud-infiltrator/TOOLS.md +68 -0
  28. package/agents/coding-assistant/CONFIG.yaml +22 -0
  29. package/agents/coding-assistant/HEARTBEAT.md +57 -0
  30. package/agents/coding-assistant/IDENTITY.md +5 -0
  31. package/agents/coding-assistant/SKILLS.md +69 -0
  32. package/agents/coding-assistant/SOUL.md +60 -0
  33. package/agents/coding-assistant/TOOLS.md +168 -0
  34. package/agents/learning-agent/CONFIG.yaml +21 -0
  35. package/agents/learning-agent/HEARTBEAT.md +63 -0
  36. package/agents/learning-agent/IDENTITY.md +5 -0
  37. package/agents/learning-agent/SKILLS.md +86 -0
  38. package/agents/learning-agent/SOUL.md +77 -0
  39. package/agents/learning-agent/TOOLS.md +145 -0
  40. package/agents/maintainer/CONFIG.yaml +31 -0
  41. package/agents/maintainer/HEARTBEAT.md +28 -0
  42. package/agents/maintainer/IDENTITY.md +33 -0
  43. package/agents/maintainer/SKILLS.md +24 -0
  44. package/agents/maintainer/SOUL.md +61 -0
  45. package/agents/maintainer/TOOLS.md +29 -0
  46. package/agents/maintainer/lib/engine.js +279 -0
  47. package/agents/maintainer/lib/safe-fixer.js +183 -0
  48. package/agents/morning-brief/CONFIG.yaml +22 -0
  49. package/agents/morning-brief/HEARTBEAT.md +60 -0
  50. package/agents/morning-brief/IDENTITY.md +5 -0
  51. package/agents/morning-brief/SKILLS.md +56 -0
  52. package/agents/morning-brief/SOUL.md +64 -0
  53. package/agents/morning-brief/TOOLS.md +112 -0
  54. package/agents/osint-detective/CONFIG.yaml +24 -0
  55. package/agents/osint-detective/HEARTBEAT.md +66 -0
  56. package/agents/osint-detective/IDENTITY.md +1 -0
  57. package/agents/osint-detective/SKILLS.md +1 -0
  58. package/agents/osint-detective/SOUL.md +23 -0
  59. package/agents/osint-detective/TOOLS.md +81 -0
  60. package/agents/recon-scout/CONFIG.yaml +22 -0
  61. package/agents/recon-scout/HEARTBEAT.md +79 -0
  62. package/agents/recon-scout/IDENTITY.md +1 -0
  63. package/agents/recon-scout/SKILLS.md +1 -0
  64. package/agents/recon-scout/SOUL.md +23 -0
  65. package/agents/recon-scout/TOOLS.md +93 -0
  66. package/agents/report-writer/CONFIG.yaml +21 -0
  67. package/agents/report-writer/HEARTBEAT.md +63 -0
  68. package/agents/report-writer/IDENTITY.md +1 -0
  69. package/agents/report-writer/SKILLS.md +1 -0
  70. package/agents/report-writer/SOUL.md +23 -0
  71. package/agents/report-writer/TOOLS.md +69 -0
  72. package/agents/shared/README.md +13 -0
  73. package/agents/web-hacker/CONFIG.yaml +24 -0
  74. package/agents/web-hacker/HEARTBEAT.md +78 -0
  75. package/agents/web-hacker/IDENTITY.md +1 -0
  76. package/agents/web-hacker/SKILLS.md +1 -0
  77. package/agents/web-hacker/SOUL.md +23 -0
  78. package/agents/web-hacker/TOOLS.md +86 -0
  79. package/api/CLAUDE.md +19 -0
  80. package/api/index.js +274 -0
  81. package/bin/cli.js +620 -0
  82. package/bin/local.sh +31 -0
  83. package/bin/postinstall.js +63 -0
  84. package/config/index.js +24 -0
  85. package/config/instrumentation.js +93 -0
  86. package/drizzle/0000_initial.sql +52 -0
  87. package/drizzle/0001_bounty_and_registry.sql +82 -0
  88. package/drizzle/0002_sync_columns.sql +7 -0
  89. package/drizzle/0003_graceful_bloodscream.sql +86 -0
  90. package/drizzle/meta/0000_snapshot.json +321 -0
  91. package/drizzle/meta/0003_snapshot.json +878 -0
  92. package/drizzle/meta/_journal.json +34 -0
  93. package/drizzle/relations.ts +3 -0
  94. package/drizzle/schema.ts +145 -0
  95. package/lib/actions.js +47 -0
  96. package/lib/agents.js +166 -0
  97. package/lib/ai/agent.js +96 -0
  98. package/lib/ai/autonomous-engine.js +261 -0
  99. package/lib/ai/index.js +359 -0
  100. package/lib/ai/model-router.js +254 -0
  101. package/lib/ai/model.js +73 -0
  102. package/lib/ai/tools.js +84 -0
  103. package/lib/auth/actions.js +28 -0
  104. package/lib/auth/config.js +27 -0
  105. package/lib/auth/edge-config.js +27 -0
  106. package/lib/auth/index.js +27 -0
  107. package/lib/auth/middleware.js +53 -0
  108. package/lib/bounty/actions.js +119 -0
  109. package/lib/bounty/findings.js +64 -0
  110. package/lib/bounty/programs.js +34 -0
  111. package/lib/bounty/sync-targets.js +267 -0
  112. package/lib/bounty/targets.js +33 -0
  113. package/lib/channels/base.js +56 -0
  114. package/lib/channels/index.js +15 -0
  115. package/lib/channels/telegram.js +148 -0
  116. package/lib/chat/actions.js +288 -0
  117. package/lib/chat/api.js +135 -0
  118. package/lib/chat/components/app-sidebar.js +237 -0
  119. package/lib/chat/components/app-sidebar.jsx +289 -0
  120. package/lib/chat/components/chat-header.js +27 -0
  121. package/lib/chat/components/chat-header.jsx +37 -0
  122. package/lib/chat/components/chat-input.js +230 -0
  123. package/lib/chat/components/chat-input.jsx +228 -0
  124. package/lib/chat/components/chat-nav-context.js +11 -0
  125. package/lib/chat/components/chat-nav-context.jsx +11 -0
  126. package/lib/chat/components/chat-page.js +81 -0
  127. package/lib/chat/components/chat-page.jsx +100 -0
  128. package/lib/chat/components/chat.js +150 -0
  129. package/lib/chat/components/chat.jsx +182 -0
  130. package/lib/chat/components/chats-page.js +302 -0
  131. package/lib/chat/components/chats-page.jsx +330 -0
  132. package/lib/chat/components/crons-page.js +172 -0
  133. package/lib/chat/components/crons-page.jsx +244 -0
  134. package/lib/chat/components/enhanced-tool-call.js +103 -0
  135. package/lib/chat/components/enhanced-tool-call.jsx +139 -0
  136. package/lib/chat/components/findings-page.js +175 -0
  137. package/lib/chat/components/findings-page.jsx +214 -0
  138. package/lib/chat/components/greeting.js +22 -0
  139. package/lib/chat/components/greeting.jsx +26 -0
  140. package/lib/chat/components/icons.js +777 -0
  141. package/lib/chat/components/icons.jsx +741 -0
  142. package/lib/chat/components/index.js +26 -0
  143. package/lib/chat/components/mcp-page.js +260 -0
  144. package/lib/chat/components/mcp-page.jsx +355 -0
  145. package/lib/chat/components/message.js +289 -0
  146. package/lib/chat/components/message.jsx +315 -0
  147. package/lib/chat/components/messages.js +66 -0
  148. package/lib/chat/components/messages.jsx +77 -0
  149. package/lib/chat/components/notifications-page.js +56 -0
  150. package/lib/chat/components/notifications-page.jsx +87 -0
  151. package/lib/chat/components/page-layout.js +21 -0
  152. package/lib/chat/components/page-layout.jsx +28 -0
  153. package/lib/chat/components/registry-page.js +222 -0
  154. package/lib/chat/components/registry-page.jsx +255 -0
  155. package/lib/chat/components/settings-layout.js +40 -0
  156. package/lib/chat/components/settings-layout.jsx +54 -0
  157. package/lib/chat/components/settings-secrets-page.js +216 -0
  158. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  159. package/lib/chat/components/sidebar-history-item.js +132 -0
  160. package/lib/chat/components/sidebar-history-item.jsx +113 -0
  161. package/lib/chat/components/sidebar-history.js +115 -0
  162. package/lib/chat/components/sidebar-history.jsx +157 -0
  163. package/lib/chat/components/sidebar-user-nav.js +63 -0
  164. package/lib/chat/components/sidebar-user-nav.jsx +73 -0
  165. package/lib/chat/components/status-bar.js +39 -0
  166. package/lib/chat/components/status-bar.jsx +51 -0
  167. package/lib/chat/components/swarm-page.js +157 -0
  168. package/lib/chat/components/swarm-page.jsx +210 -0
  169. package/lib/chat/components/targets-page.js +376 -0
  170. package/lib/chat/components/targets-page.jsx +389 -0
  171. package/lib/chat/components/tool-call.js +86 -0
  172. package/lib/chat/components/tool-call.jsx +104 -0
  173. package/lib/chat/components/tool-panel.js +107 -0
  174. package/lib/chat/components/tool-panel.jsx +145 -0
  175. package/lib/chat/components/triggers-page.js +153 -0
  176. package/lib/chat/components/triggers-page.jsx +221 -0
  177. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  178. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  179. package/lib/chat/components/ui/dropdown-menu.js +98 -0
  180. package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
  181. package/lib/chat/components/ui/rename-dialog.js +74 -0
  182. package/lib/chat/components/ui/rename-dialog.jsx +72 -0
  183. package/lib/chat/components/ui/scroll-area.js +13 -0
  184. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  185. package/lib/chat/components/ui/separator.js +21 -0
  186. package/lib/chat/components/ui/separator.jsx +18 -0
  187. package/lib/chat/components/ui/sheet.js +75 -0
  188. package/lib/chat/components/ui/sheet.jsx +95 -0
  189. package/lib/chat/components/ui/sidebar.js +227 -0
  190. package/lib/chat/components/ui/sidebar.jsx +245 -0
  191. package/lib/chat/components/ui/tooltip.js +56 -0
  192. package/lib/chat/components/ui/tooltip.jsx +66 -0
  193. package/lib/chat/components/upgrade-dialog.js +151 -0
  194. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  195. package/lib/chat/utils.js +11 -0
  196. package/lib/cron.js +246 -0
  197. package/lib/db/api-keys.js +163 -0
  198. package/lib/db/chats.js +145 -0
  199. package/lib/db/index.js +52 -0
  200. package/lib/db/notifications.js +99 -0
  201. package/lib/db/schema.js +145 -0
  202. package/lib/db/update-check.js +96 -0
  203. package/lib/db/users.js +89 -0
  204. package/lib/mcp/actions.js +104 -0
  205. package/lib/mcp/client.js +79 -0
  206. package/lib/mcp/handler.js +57 -0
  207. package/lib/mcp/server.js +165 -0
  208. package/lib/paths.js +46 -0
  209. package/lib/registry/actions.js +164 -0
  210. package/lib/registry/catalog.js +137 -0
  211. package/lib/registry/tools.js +71 -0
  212. package/lib/tools/create-job.js +99 -0
  213. package/lib/tools/github.js +217 -0
  214. package/lib/tools/openai.js +35 -0
  215. package/lib/tools/telegram.js +292 -0
  216. package/lib/triggers.js +118 -0
  217. package/lib/utils/render-md.js +102 -0
  218. package/package.json +103 -0
  219. package/setup/lib/auth.mjs +81 -0
  220. package/setup/lib/env.mjs +21 -0
  221. package/setup/lib/fs-utils.mjs +20 -0
  222. package/setup/lib/github.mjs +149 -0
  223. package/setup/lib/prerequisites.mjs +155 -0
  224. package/setup/lib/prompts.mjs +267 -0
  225. package/setup/lib/providers.mjs +48 -0
  226. package/setup/lib/sync.mjs +125 -0
  227. package/setup/lib/targets.mjs +45 -0
  228. package/setup/lib/telegram-verify.mjs +63 -0
  229. package/setup/lib/telegram.mjs +76 -0
  230. package/setup/setup-telegram.mjs +264 -0
  231. package/setup/setup.mjs +842 -0
  232. package/templates/.dockerignore +5 -0
  233. package/templates/.env.example +63 -0
  234. package/templates/.github/workflows/auto-merge.yml +117 -0
  235. package/templates/.github/workflows/build-image.yml +36 -0
  236. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  237. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  238. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  239. package/templates/.github/workflows/run-job.yml +89 -0
  240. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  241. package/templates/.gitignore.template +45 -0
  242. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  243. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  244. package/templates/CLAUDE.md +29 -0
  245. package/templates/CLAUDE.md.template +307 -0
  246. package/templates/app/api/[...thepopebot]/route.js +1 -0
  247. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  248. package/templates/app/chat/[chatId]/page.js +8 -0
  249. package/templates/app/chats/page.js +7 -0
  250. package/templates/app/components/ascii-logo.jsx +10 -0
  251. package/templates/app/components/login-form.jsx +92 -0
  252. package/templates/app/components/setup-form.jsx +82 -0
  253. package/templates/app/components/theme-provider.jsx +11 -0
  254. package/templates/app/components/theme-toggle.jsx +38 -0
  255. package/templates/app/components/ui/button.jsx +21 -0
  256. package/templates/app/components/ui/card.jsx +23 -0
  257. package/templates/app/components/ui/input.jsx +10 -0
  258. package/templates/app/components/ui/label.jsx +10 -0
  259. package/templates/app/crons/page.js +5 -0
  260. package/templates/app/findings/page.js +7 -0
  261. package/templates/app/globals.css +90 -0
  262. package/templates/app/layout.js +19 -0
  263. package/templates/app/login/page.js +15 -0
  264. package/templates/app/notifications/page.js +7 -0
  265. package/templates/app/page.js +7 -0
  266. package/templates/app/settings/crons/page.js +5 -0
  267. package/templates/app/settings/layout.js +7 -0
  268. package/templates/app/settings/mcp/page.js +5 -0
  269. package/templates/app/settings/page.js +5 -0
  270. package/templates/app/settings/secrets/page.js +5 -0
  271. package/templates/app/settings/triggers/page.js +5 -0
  272. package/templates/app/stream/chat/route.js +1 -0
  273. package/templates/app/swarm/page.js +7 -0
  274. package/templates/app/targets/page.js +7 -0
  275. package/templates/app/toolbox/page.js +7 -0
  276. package/templates/app/triggers/page.js +5 -0
  277. package/templates/config/AGENT.md +34 -0
  278. package/templates/config/CRONS.json +56 -0
  279. package/templates/config/EVENT_HANDLER.md +224 -0
  280. package/templates/config/HEARTBEAT.md +3 -0
  281. package/templates/config/JOB_SUMMARY.md +130 -0
  282. package/templates/config/MCP_SERVERS.json +1 -0
  283. package/templates/config/SKILL_BUILDING_GUIDE.md +90 -0
  284. package/templates/config/SOUL.md +17 -0
  285. package/templates/config/TRIGGERS.json +58 -0
  286. package/templates/docker/event-handler/Dockerfile +20 -0
  287. package/templates/docker/event-handler/ecosystem.config.cjs +8 -0
  288. package/templates/docker/job-claude-code/Dockerfile +34 -0
  289. package/templates/docker/job-claude-code/entrypoint.sh +139 -0
  290. package/templates/docker/job-pi-coding-agent/Dockerfile +44 -0
  291. package/templates/docker/job-pi-coding-agent/entrypoint.sh +163 -0
  292. package/templates/docker-compose.yml +63 -0
  293. package/templates/instrumentation.js +6 -0
  294. package/templates/middleware.js +1 -0
  295. package/templates/next.config.mjs +3 -0
  296. package/templates/postcss.config.mjs +5 -0
  297. package/templates/skills/LICENSE +21 -0
  298. package/templates/skills/README.md +119 -0
  299. package/templates/skills/brave-search/SKILL.md +79 -0
  300. package/templates/skills/brave-search/content.js +86 -0
  301. package/templates/skills/brave-search/package-lock.json +621 -0
  302. package/templates/skills/brave-search/package.json +14 -0
  303. package/templates/skills/brave-search/search.js +199 -0
  304. package/templates/skills/browser-tools/SKILL.md +196 -0
  305. package/templates/skills/browser-tools/browser-content.js +103 -0
  306. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  307. package/templates/skills/browser-tools/browser-eval.js +53 -0
  308. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  309. package/templates/skills/browser-tools/browser-nav.js +44 -0
  310. package/templates/skills/browser-tools/browser-pick.js +162 -0
  311. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  312. package/templates/skills/browser-tools/browser-start.js +87 -0
  313. package/templates/skills/browser-tools/package-lock.json +2556 -0
  314. package/templates/skills/browser-tools/package.json +19 -0
  315. package/templates/skills/llm-secrets/SKILL.md +34 -0
  316. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  317. package/templates/skills/modify-self/SKILL.md +12 -0
@@ -0,0 +1,95 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, useEffect } from 'react';
4
+ import { cn } from '../../utils.js';
5
+
6
+ const SheetContext = createContext({ open: false, onOpenChange: () => {} });
7
+
8
+ export function Sheet({ children, open, onOpenChange }) {
9
+ return (
10
+ <SheetContext.Provider value={{ open, onOpenChange }}>
11
+ {children}
12
+ </SheetContext.Provider>
13
+ );
14
+ }
15
+
16
+ export function SheetTrigger({ children, asChild, ...props }) {
17
+ const { onOpenChange } = useContext(SheetContext);
18
+ if (asChild && children) {
19
+ return (
20
+ <span onClick={() => onOpenChange(true)} {...props}>
21
+ {children}
22
+ </span>
23
+ );
24
+ }
25
+ return (
26
+ <button onClick={() => onOpenChange(true)} {...props}>
27
+ {children}
28
+ </button>
29
+ );
30
+ }
31
+
32
+ export function SheetContent({ children, className, side = 'left', ...props }) {
33
+ const { open, onOpenChange } = useContext(SheetContext);
34
+
35
+ useEffect(() => {
36
+ if (!open) return;
37
+ const handleEsc = (e) => {
38
+ if (e.key === 'Escape') onOpenChange(false);
39
+ };
40
+ document.addEventListener('keydown', handleEsc);
41
+ return () => document.removeEventListener('keydown', handleEsc);
42
+ }, [open, onOpenChange]);
43
+
44
+ if (!open) return null;
45
+
46
+ return (
47
+ <div className="fixed inset-0 z-50">
48
+ {/* Overlay */}
49
+ <div
50
+ className="fixed inset-0 bg-black/50"
51
+ onClick={() => onOpenChange(false)}
52
+ />
53
+ {/* Content */}
54
+ <div
55
+ className={cn(
56
+ 'fixed z-50 bg-background shadow-lg transition-transform',
57
+ side === 'left' && 'inset-y-0 left-0 w-3/4 max-w-sm border-r border-border',
58
+ side === 'right' && 'inset-y-0 right-0 w-3/4 max-w-sm border-l border-border',
59
+ className
60
+ )}
61
+ {...props}
62
+ >
63
+ {children}
64
+ </div>
65
+ </div>
66
+ );
67
+ }
68
+
69
+ export function SheetHeader({ children, className }) {
70
+ return <div className={cn('flex flex-col space-y-2 p-4', className)}>{children}</div>;
71
+ }
72
+
73
+ export function SheetTitle({ children, className }) {
74
+ return <h2 className={cn('text-lg font-semibold text-foreground', className)}>{children}</h2>;
75
+ }
76
+
77
+ export function SheetDescription({ children, className }) {
78
+ return <p className={cn('text-sm text-muted-foreground', className)}>{children}</p>;
79
+ }
80
+
81
+ export function SheetClose({ children, asChild, ...props }) {
82
+ const { onOpenChange } = useContext(SheetContext);
83
+ if (asChild && children) {
84
+ return (
85
+ <span onClick={() => onOpenChange(false)} {...props}>
86
+ {children}
87
+ </span>
88
+ );
89
+ }
90
+ return (
91
+ <button onClick={() => onOpenChange(false)} {...props}>
92
+ {children}
93
+ </button>
94
+ );
95
+ }
@@ -0,0 +1,227 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { createContext, useContext, useState, useEffect, useCallback, useMemo } from "react";
4
+ import { cn } from "../../utils.js";
5
+ import { Sheet, SheetContent } from "./sheet.js";
6
+ const SIDEBAR_WIDTH = "16rem";
7
+ const SIDEBAR_WIDTH_ICON = "3rem";
8
+ const SIDEBAR_WIDTH_MOBILE = "18rem";
9
+ const SIDEBAR_COOKIE_NAME = "sidebar:state";
10
+ const SIDEBAR_KEYBOARD_SHORTCUT = "b";
11
+ const SidebarContext = createContext(null);
12
+ function useSidebar() {
13
+ const context = useContext(SidebarContext);
14
+ if (!context) {
15
+ throw new Error("useSidebar must be used within a SidebarProvider");
16
+ }
17
+ return context;
18
+ }
19
+ function SidebarProvider({
20
+ children,
21
+ defaultOpen = true,
22
+ open: openProp,
23
+ onOpenChange: setOpenProp
24
+ }) {
25
+ const [isMobile, setIsMobile] = useState(false);
26
+ const [openMobile, setOpenMobile] = useState(false);
27
+ const [_open, _setOpen] = useState(defaultOpen);
28
+ const open = openProp !== void 0 ? openProp : _open;
29
+ const setOpen = useCallback(
30
+ (value) => {
31
+ const newOpen = typeof value === "function" ? value(open) : value;
32
+ if (setOpenProp) {
33
+ setOpenProp(newOpen);
34
+ } else {
35
+ _setOpen(newOpen);
36
+ }
37
+ try {
38
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${newOpen}; path=/; max-age=${60 * 60 * 24 * 7}`;
39
+ } catch (e) {
40
+ }
41
+ },
42
+ [setOpenProp, open]
43
+ );
44
+ const toggleSidebar = useCallback(() => {
45
+ if (isMobile) {
46
+ setOpenMobile((prev) => !prev);
47
+ } else {
48
+ setOpen((prev) => !prev);
49
+ }
50
+ }, [isMobile, setOpen]);
51
+ useEffect(() => {
52
+ const check = () => setIsMobile(window.innerWidth < 768);
53
+ check();
54
+ window.addEventListener("resize", check);
55
+ return () => window.removeEventListener("resize", check);
56
+ }, []);
57
+ useEffect(() => {
58
+ const handleKeyDown = (e) => {
59
+ if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) {
60
+ e.preventDefault();
61
+ toggleSidebar();
62
+ }
63
+ };
64
+ window.addEventListener("keydown", handleKeyDown);
65
+ return () => window.removeEventListener("keydown", handleKeyDown);
66
+ }, [toggleSidebar]);
67
+ const state = open ? "expanded" : "collapsed";
68
+ const contextValue = useMemo(
69
+ () => ({
70
+ state,
71
+ open,
72
+ setOpen,
73
+ isMobile,
74
+ openMobile,
75
+ setOpenMobile,
76
+ toggleSidebar
77
+ }),
78
+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
79
+ );
80
+ return /* @__PURE__ */ jsx(SidebarContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
81
+ "div",
82
+ {
83
+ className: "group/sidebar-wrapper flex min-h-svh w-full",
84
+ style: {
85
+ "--sidebar-width": SIDEBAR_WIDTH,
86
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
87
+ "--sidebar-width-mobile": SIDEBAR_WIDTH_MOBILE
88
+ },
89
+ "data-sidebar-state": state,
90
+ children
91
+ }
92
+ ) });
93
+ }
94
+ function Sidebar({ children, className, side = "left" }) {
95
+ const { isMobile, open, openMobile, setOpenMobile } = useSidebar();
96
+ if (isMobile) {
97
+ return /* @__PURE__ */ jsx(Sheet, { open: openMobile, onOpenChange: setOpenMobile, children: /* @__PURE__ */ jsx(
98
+ SheetContent,
99
+ {
100
+ side,
101
+ className: cn("w-[var(--sidebar-width-mobile)] p-0 [&>button]:hidden", className),
102
+ children: /* @__PURE__ */ jsx("div", { className: "flex h-full w-full flex-col", children })
103
+ }
104
+ ) });
105
+ }
106
+ return /* @__PURE__ */ jsx(
107
+ "div",
108
+ {
109
+ className: cn(
110
+ "sticky top-0 flex h-svh flex-col border-r border-border bg-muted transition-[width] duration-200",
111
+ open ? "w-[var(--sidebar-width)]" : "w-[var(--sidebar-width-icon)]",
112
+ className
113
+ ),
114
+ children: /* @__PURE__ */ jsx(
115
+ "div",
116
+ {
117
+ className: cn(
118
+ "flex h-full flex-col overflow-hidden",
119
+ open ? "w-[var(--sidebar-width)]" : "w-[var(--sidebar-width-icon)]"
120
+ ),
121
+ children
122
+ }
123
+ )
124
+ }
125
+ );
126
+ }
127
+ function SidebarHeader({ children, className }) {
128
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-2 p-2", className), children });
129
+ }
130
+ function SidebarContent({ children, className }) {
131
+ return /* @__PURE__ */ jsx("div", { className: cn("flex min-h-0 flex-1 flex-col overflow-y-auto", className), children });
132
+ }
133
+ function SidebarFooter({ children, className }) {
134
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-2 p-2", className), children });
135
+ }
136
+ function SidebarMenu({ children, className }) {
137
+ return /* @__PURE__ */ jsx("ul", { className: cn("flex w-full min-w-0 flex-col gap-1", className), children });
138
+ }
139
+ function SidebarMenuItem({ children, className }) {
140
+ return /* @__PURE__ */ jsx("li", { className: cn("group/menu-item relative", className), children });
141
+ }
142
+ function SidebarMenuButton({ children, className, isActive, asChild, tooltip, ...props }) {
143
+ const Tag = asChild ? "span" : "button";
144
+ return /* @__PURE__ */ jsx(
145
+ Tag,
146
+ {
147
+ className: cn(
148
+ "flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none transition-colors",
149
+ "hover:bg-background hover:text-foreground",
150
+ isActive && "bg-background text-foreground font-medium",
151
+ className
152
+ ),
153
+ ...props,
154
+ children
155
+ }
156
+ );
157
+ }
158
+ function SidebarGroup({ children, className }) {
159
+ return /* @__PURE__ */ jsx("div", { className: cn("relative flex w-full min-w-0 flex-col p-2", className), children });
160
+ }
161
+ function SidebarGroupLabel({ children, className }) {
162
+ return /* @__PURE__ */ jsx(
163
+ "div",
164
+ {
165
+ className: cn(
166
+ "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-muted-foreground",
167
+ className
168
+ ),
169
+ children
170
+ }
171
+ );
172
+ }
173
+ function SidebarGroupContent({ children, className }) {
174
+ return /* @__PURE__ */ jsx("div", { className: cn("w-full", className), children });
175
+ }
176
+ function SidebarInset({ children, className }) {
177
+ return /* @__PURE__ */ jsx("main", { className: cn("relative flex min-h-svh flex-1 flex-col bg-background", className), children });
178
+ }
179
+ function SidebarTrigger({ className, ...props }) {
180
+ const { toggleSidebar } = useSidebar();
181
+ return /* @__PURE__ */ jsxs(
182
+ "button",
183
+ {
184
+ className: cn(
185
+ "inline-flex items-center justify-center rounded-md p-2 text-foreground hover:bg-muted",
186
+ className
187
+ ),
188
+ onClick: toggleSidebar,
189
+ ...props,
190
+ children: [
191
+ /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className: "size-4", children: [
192
+ /* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
193
+ /* @__PURE__ */ jsx("path", { d: "M9 3v18" })
194
+ ] }),
195
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Toggle Sidebar" })
196
+ ]
197
+ }
198
+ );
199
+ }
200
+ function SidebarRail() {
201
+ const { toggleSidebar } = useSidebar();
202
+ return /* @__PURE__ */ jsx(
203
+ "button",
204
+ {
205
+ className: "absolute inset-y-0 right-0 w-1 cursor-col-resize hover:bg-border",
206
+ onClick: toggleSidebar,
207
+ "aria-label": "Toggle Sidebar"
208
+ }
209
+ );
210
+ }
211
+ export {
212
+ Sidebar,
213
+ SidebarContent,
214
+ SidebarFooter,
215
+ SidebarGroup,
216
+ SidebarGroupContent,
217
+ SidebarGroupLabel,
218
+ SidebarHeader,
219
+ SidebarInset,
220
+ SidebarMenu,
221
+ SidebarMenuButton,
222
+ SidebarMenuItem,
223
+ SidebarProvider,
224
+ SidebarRail,
225
+ SidebarTrigger,
226
+ useSidebar
227
+ };
@@ -0,0 +1,245 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react';
4
+ import { cn } from '../../utils.js';
5
+ import { Sheet, SheetContent } from './sheet.js';
6
+
7
+ const SIDEBAR_WIDTH = '16rem';
8
+ const SIDEBAR_WIDTH_ICON = '3rem';
9
+ const SIDEBAR_WIDTH_MOBILE = '18rem';
10
+ const SIDEBAR_COOKIE_NAME = 'sidebar:state';
11
+ const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
12
+
13
+ const SidebarContext = createContext(null);
14
+
15
+ export function useSidebar() {
16
+ const context = useContext(SidebarContext);
17
+ if (!context) {
18
+ throw new Error('useSidebar must be used within a SidebarProvider');
19
+ }
20
+ return context;
21
+ }
22
+
23
+ export function SidebarProvider({
24
+ children,
25
+ defaultOpen = true,
26
+ open: openProp,
27
+ onOpenChange: setOpenProp,
28
+ }) {
29
+ const [isMobile, setIsMobile] = useState(false);
30
+ const [openMobile, setOpenMobile] = useState(false);
31
+ const [_open, _setOpen] = useState(defaultOpen);
32
+ const open = openProp !== undefined ? openProp : _open;
33
+ const setOpen = useCallback(
34
+ (value) => {
35
+ const newOpen = typeof value === 'function' ? value(open) : value;
36
+ if (setOpenProp) {
37
+ setOpenProp(newOpen);
38
+ } else {
39
+ _setOpen(newOpen);
40
+ }
41
+ try {
42
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${newOpen}; path=/; max-age=${60 * 60 * 24 * 7}`;
43
+ } catch (e) {
44
+ // SSR safety
45
+ }
46
+ },
47
+ [setOpenProp, open]
48
+ );
49
+
50
+ const toggleSidebar = useCallback(() => {
51
+ if (isMobile) {
52
+ setOpenMobile((prev) => !prev);
53
+ } else {
54
+ setOpen((prev) => !prev);
55
+ }
56
+ }, [isMobile, setOpen]);
57
+
58
+ // Detect mobile
59
+ useEffect(() => {
60
+ const check = () => setIsMobile(window.innerWidth < 768);
61
+ check();
62
+ window.addEventListener('resize', check);
63
+ return () => window.removeEventListener('resize', check);
64
+ }, []);
65
+
66
+ // Keyboard shortcut (Cmd/Ctrl + B)
67
+ useEffect(() => {
68
+ const handleKeyDown = (e) => {
69
+ if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) {
70
+ e.preventDefault();
71
+ toggleSidebar();
72
+ }
73
+ };
74
+ window.addEventListener('keydown', handleKeyDown);
75
+ return () => window.removeEventListener('keydown', handleKeyDown);
76
+ }, [toggleSidebar]);
77
+
78
+ const state = open ? 'expanded' : 'collapsed';
79
+
80
+ const contextValue = useMemo(
81
+ () => ({
82
+ state,
83
+ open,
84
+ setOpen,
85
+ isMobile,
86
+ openMobile,
87
+ setOpenMobile,
88
+ toggleSidebar,
89
+ }),
90
+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
91
+ );
92
+
93
+ return (
94
+ <SidebarContext.Provider value={contextValue}>
95
+ <div
96
+ className="group/sidebar-wrapper flex min-h-svh w-full"
97
+ style={{
98
+ '--sidebar-width': SIDEBAR_WIDTH,
99
+ '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
100
+ '--sidebar-width-mobile': SIDEBAR_WIDTH_MOBILE,
101
+ }}
102
+ data-sidebar-state={state}
103
+ >
104
+ {children}
105
+ </div>
106
+ </SidebarContext.Provider>
107
+ );
108
+ }
109
+
110
+ export function Sidebar({ children, className, side = 'left' }) {
111
+ const { isMobile, open, openMobile, setOpenMobile } = useSidebar();
112
+
113
+ if (isMobile) {
114
+ return (
115
+ <Sheet open={openMobile} onOpenChange={setOpenMobile}>
116
+ <SheetContent
117
+ side={side}
118
+ className={cn('w-[var(--sidebar-width-mobile)] p-0 [&>button]:hidden', className)}
119
+ >
120
+ <div className="flex h-full w-full flex-col">{children}</div>
121
+ </SheetContent>
122
+ </Sheet>
123
+ );
124
+ }
125
+
126
+ return (
127
+ <div
128
+ className={cn(
129
+ 'sticky top-0 flex h-svh flex-col border-r border-border bg-muted transition-[width] duration-200',
130
+ open ? 'w-[var(--sidebar-width)]' : 'w-[var(--sidebar-width-icon)]',
131
+ className
132
+ )}
133
+ >
134
+ <div
135
+ className={cn(
136
+ 'flex h-full flex-col overflow-hidden',
137
+ open ? 'w-[var(--sidebar-width)]' : 'w-[var(--sidebar-width-icon)]'
138
+ )}
139
+ >
140
+ {children}
141
+ </div>
142
+ </div>
143
+ );
144
+ }
145
+
146
+ export function SidebarHeader({ children, className }) {
147
+ return <div className={cn('flex flex-col gap-2 p-2', className)}>{children}</div>;
148
+ }
149
+
150
+ export function SidebarContent({ children, className }) {
151
+ return (
152
+ <div className={cn('flex min-h-0 flex-1 flex-col overflow-y-auto', className)}>
153
+ {children}
154
+ </div>
155
+ );
156
+ }
157
+
158
+ export function SidebarFooter({ children, className }) {
159
+ return <div className={cn('flex flex-col gap-2 p-2', className)}>{children}</div>;
160
+ }
161
+
162
+ export function SidebarMenu({ children, className }) {
163
+ return <ul className={cn('flex w-full min-w-0 flex-col gap-1', className)}>{children}</ul>;
164
+ }
165
+
166
+ export function SidebarMenuItem({ children, className }) {
167
+ return <li className={cn('group/menu-item relative', className)}>{children}</li>;
168
+ }
169
+
170
+ export function SidebarMenuButton({ children, className, isActive, asChild, tooltip, ...props }) {
171
+ const Tag = asChild ? 'span' : 'button';
172
+ return (
173
+ <Tag
174
+ className={cn(
175
+ 'flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none transition-colors',
176
+ 'hover:bg-background hover:text-foreground',
177
+ isActive && 'bg-background text-foreground font-medium',
178
+ className
179
+ )}
180
+ {...props}
181
+ >
182
+ {children}
183
+ </Tag>
184
+ );
185
+ }
186
+
187
+ export function SidebarGroup({ children, className }) {
188
+ return <div className={cn('relative flex w-full min-w-0 flex-col p-2', className)}>{children}</div>;
189
+ }
190
+
191
+ export function SidebarGroupLabel({ children, className }) {
192
+ return (
193
+ <div
194
+ className={cn(
195
+ 'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-muted-foreground',
196
+ className
197
+ )}
198
+ >
199
+ {children}
200
+ </div>
201
+ );
202
+ }
203
+
204
+ export function SidebarGroupContent({ children, className }) {
205
+ return <div className={cn('w-full', className)}>{children}</div>;
206
+ }
207
+
208
+ export function SidebarInset({ children, className }) {
209
+ return (
210
+ <main className={cn('relative flex min-h-svh flex-1 flex-col bg-background', className)}>
211
+ {children}
212
+ </main>
213
+ );
214
+ }
215
+
216
+ export function SidebarTrigger({ className, ...props }) {
217
+ const { toggleSidebar } = useSidebar();
218
+ return (
219
+ <button
220
+ className={cn(
221
+ 'inline-flex items-center justify-center rounded-md p-2 text-foreground hover:bg-muted',
222
+ className
223
+ )}
224
+ onClick={toggleSidebar}
225
+ {...props}
226
+ >
227
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" className="size-4">
228
+ <rect width="18" height="18" x="3" y="3" rx="2" />
229
+ <path d="M9 3v18" />
230
+ </svg>
231
+ <span className="sr-only">Toggle Sidebar</span>
232
+ </button>
233
+ );
234
+ }
235
+
236
+ export function SidebarRail() {
237
+ const { toggleSidebar } = useSidebar();
238
+ return (
239
+ <button
240
+ className="absolute inset-y-0 right-0 w-1 cursor-col-resize hover:bg-border"
241
+ onClick={toggleSidebar}
242
+ aria-label="Toggle Sidebar"
243
+ />
244
+ );
245
+ }
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useState, useRef, useEffect, createContext, useContext, cloneElement } from "react";
4
+ import { cn } from "../../utils.js";
5
+ const TooltipContext = createContext({ open: false });
6
+ function TooltipProvider({ children, delayDuration = 200 }) {
7
+ return children;
8
+ }
9
+ function Tooltip({ children }) {
10
+ const [open, setOpen] = useState(false);
11
+ const timeoutRef = useRef(null);
12
+ const handleOpen = () => {
13
+ timeoutRef.current = setTimeout(() => setOpen(true), 200);
14
+ };
15
+ const handleClose = () => {
16
+ clearTimeout(timeoutRef.current);
17
+ setOpen(false);
18
+ };
19
+ useEffect(() => () => clearTimeout(timeoutRef.current), []);
20
+ return /* @__PURE__ */ jsx(TooltipContext.Provider, { value: { open, handleOpen, handleClose }, children: /* @__PURE__ */ jsx("div", { className: "relative inline-flex", onMouseEnter: handleOpen, onMouseLeave: handleClose, children }) });
21
+ }
22
+ function TooltipTrigger({ children, asChild }) {
23
+ if (asChild && children) {
24
+ return children;
25
+ }
26
+ return children;
27
+ }
28
+ function TooltipContent({ children, className, align = "center", side = "bottom", ...props }) {
29
+ const { open } = useContext(TooltipContext);
30
+ if (!open) return null;
31
+ return /* @__PURE__ */ jsx(
32
+ "div",
33
+ {
34
+ className: cn(
35
+ "absolute z-50 overflow-hidden rounded-md border border-border bg-muted px-3 py-1.5 text-sm text-foreground shadow-md",
36
+ "animate-in fade-in-0 zoom-in-95",
37
+ side === "bottom" && "top-full mt-1",
38
+ side === "top" && "bottom-full mb-1",
39
+ side === "right" && "left-full ml-1 top-1/2 -translate-y-1/2",
40
+ side === "left" && "right-full mr-1 top-1/2 -translate-y-1/2",
41
+ side !== "right" && side !== "left" && align === "center" && "left-1/2 -translate-x-1/2",
42
+ side !== "right" && side !== "left" && align === "end" && "right-0",
43
+ side !== "right" && side !== "left" && align === "start" && "left-0",
44
+ className
45
+ ),
46
+ ...props,
47
+ children
48
+ }
49
+ );
50
+ }
51
+ export {
52
+ Tooltip,
53
+ TooltipContent,
54
+ TooltipProvider,
55
+ TooltipTrigger
56
+ };
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+
3
+ import { useState, useRef, useEffect, createContext, useContext, cloneElement } from 'react';
4
+ import { cn } from '../../utils.js';
5
+
6
+ const TooltipContext = createContext({ open: false });
7
+
8
+ export function TooltipProvider({ children, delayDuration = 200 }) {
9
+ return children;
10
+ }
11
+
12
+ export function Tooltip({ children }) {
13
+ const [open, setOpen] = useState(false);
14
+ const timeoutRef = useRef(null);
15
+
16
+ const handleOpen = () => {
17
+ timeoutRef.current = setTimeout(() => setOpen(true), 200);
18
+ };
19
+
20
+ const handleClose = () => {
21
+ clearTimeout(timeoutRef.current);
22
+ setOpen(false);
23
+ };
24
+
25
+ useEffect(() => () => clearTimeout(timeoutRef.current), []);
26
+
27
+ return (
28
+ <TooltipContext.Provider value={{ open, handleOpen, handleClose }}>
29
+ <div className="relative inline-flex" onMouseEnter={handleOpen} onMouseLeave={handleClose}>
30
+ {children}
31
+ </div>
32
+ </TooltipContext.Provider>
33
+ );
34
+ }
35
+
36
+ export function TooltipTrigger({ children, asChild }) {
37
+ if (asChild && children) {
38
+ return children;
39
+ }
40
+ return children;
41
+ }
42
+
43
+ export function TooltipContent({ children, className, align = 'center', side = 'bottom', ...props }) {
44
+ const { open } = useContext(TooltipContext);
45
+ if (!open) return null;
46
+
47
+ return (
48
+ <div
49
+ className={cn(
50
+ 'absolute z-50 overflow-hidden rounded-md border border-border bg-muted px-3 py-1.5 text-sm text-foreground shadow-md',
51
+ 'animate-in fade-in-0 zoom-in-95',
52
+ side === 'bottom' && 'top-full mt-1',
53
+ side === 'top' && 'bottom-full mb-1',
54
+ side === 'right' && 'left-full ml-1 top-1/2 -translate-y-1/2',
55
+ side === 'left' && 'right-full mr-1 top-1/2 -translate-y-1/2',
56
+ side !== 'right' && side !== 'left' && align === 'center' && 'left-1/2 -translate-x-1/2',
57
+ side !== 'right' && side !== 'left' && align === 'end' && 'right-0',
58
+ side !== 'right' && side !== 'left' && align === 'start' && 'left-0',
59
+ className
60
+ )}
61
+ {...props}
62
+ >
63
+ {children}
64
+ </div>
65
+ );
66
+ }