gigaclaw 1.4.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 (249) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +237 -0
  3. package/api/CLAUDE.md +19 -0
  4. package/api/index.js +265 -0
  5. package/bin/cli.js +823 -0
  6. package/bin/local.sh +85 -0
  7. package/bin/postinstall.js +63 -0
  8. package/config/index.js +26 -0
  9. package/config/instrumentation.js +62 -0
  10. package/drizzle/0000_initial.sql +52 -0
  11. package/drizzle/0001_nostalgic_sersi.sql +11 -0
  12. package/drizzle/0002_black_daimon_hellstrom.sql +19 -0
  13. package/drizzle/0003_rename_code_workspaces.sql +5 -0
  14. package/drizzle/meta/0000_snapshot.json +321 -0
  15. package/drizzle/meta/0001_snapshot.json +390 -0
  16. package/drizzle/meta/0002_snapshot.json +411 -0
  17. package/drizzle/meta/0003_snapshot.json +419 -0
  18. package/drizzle/meta/_journal.json +34 -0
  19. package/lib/actions.js +44 -0
  20. package/lib/ai/agent.js +86 -0
  21. package/lib/ai/index.js +342 -0
  22. package/lib/ai/model.js +180 -0
  23. package/lib/ai/tools.js +269 -0
  24. package/lib/ai/web-search.js +42 -0
  25. package/lib/auth/actions.js +28 -0
  26. package/lib/auth/config.js +27 -0
  27. package/lib/auth/edge-config.js +27 -0
  28. package/lib/auth/index.js +27 -0
  29. package/lib/auth/middleware.js +62 -0
  30. package/lib/channels/base.js +56 -0
  31. package/lib/channels/index.js +15 -0
  32. package/lib/channels/telegram.js +148 -0
  33. package/lib/chat/actions.js +579 -0
  34. package/lib/chat/api.js +140 -0
  35. package/lib/chat/components/app-sidebar.js +213 -0
  36. package/lib/chat/components/app-sidebar.jsx +279 -0
  37. package/lib/chat/components/chat-header.js +192 -0
  38. package/lib/chat/components/chat-header.jsx +223 -0
  39. package/lib/chat/components/chat-input.js +236 -0
  40. package/lib/chat/components/chat-input.jsx +249 -0
  41. package/lib/chat/components/chat-nav-context.js +11 -0
  42. package/lib/chat/components/chat-nav-context.jsx +11 -0
  43. package/lib/chat/components/chat-page.js +99 -0
  44. package/lib/chat/components/chat-page.jsx +121 -0
  45. package/lib/chat/components/chat.js +153 -0
  46. package/lib/chat/components/chat.jsx +199 -0
  47. package/lib/chat/components/chats-page.js +367 -0
  48. package/lib/chat/components/chats-page.jsx +394 -0
  49. package/lib/chat/components/code-mode-toggle.js +132 -0
  50. package/lib/chat/components/code-mode-toggle.jsx +163 -0
  51. package/lib/chat/components/crons-page.js +172 -0
  52. package/lib/chat/components/crons-page.jsx +244 -0
  53. package/lib/chat/components/greeting.js +11 -0
  54. package/lib/chat/components/greeting.jsx +16 -0
  55. package/lib/chat/components/icons.js +805 -0
  56. package/lib/chat/components/icons.jsx +751 -0
  57. package/lib/chat/components/index.js +20 -0
  58. package/lib/chat/components/message.js +363 -0
  59. package/lib/chat/components/message.jsx +422 -0
  60. package/lib/chat/components/messages.js +65 -0
  61. package/lib/chat/components/messages.jsx +74 -0
  62. package/lib/chat/components/notifications-page.js +56 -0
  63. package/lib/chat/components/notifications-page.jsx +87 -0
  64. package/lib/chat/components/page-layout.js +21 -0
  65. package/lib/chat/components/page-layout.jsx +28 -0
  66. package/lib/chat/components/pull-requests-page.js +103 -0
  67. package/lib/chat/components/pull-requests-page.jsx +113 -0
  68. package/lib/chat/components/settings-layout.js +39 -0
  69. package/lib/chat/components/settings-layout.jsx +53 -0
  70. package/lib/chat/components/settings-secrets-page.js +216 -0
  71. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  72. package/lib/chat/components/sidebar-history-item.js +138 -0
  73. package/lib/chat/components/sidebar-history-item.jsx +119 -0
  74. package/lib/chat/components/sidebar-history.js +167 -0
  75. package/lib/chat/components/sidebar-history.jsx +220 -0
  76. package/lib/chat/components/sidebar-user-nav.js +61 -0
  77. package/lib/chat/components/sidebar-user-nav.jsx +77 -0
  78. package/lib/chat/components/swarm-page.js +157 -0
  79. package/lib/chat/components/swarm-page.jsx +210 -0
  80. package/lib/chat/components/tool-call.js +89 -0
  81. package/lib/chat/components/tool-call.jsx +107 -0
  82. package/lib/chat/components/triggers-page.js +153 -0
  83. package/lib/chat/components/triggers-page.jsx +221 -0
  84. package/lib/chat/components/ui/combobox.js +98 -0
  85. package/lib/chat/components/ui/combobox.jsx +114 -0
  86. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  87. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  88. package/lib/chat/components/ui/dropdown-menu.js +194 -0
  89. package/lib/chat/components/ui/dropdown-menu.jsx +215 -0
  90. package/lib/chat/components/ui/rename-dialog.js +78 -0
  91. package/lib/chat/components/ui/rename-dialog.jsx +74 -0
  92. package/lib/chat/components/ui/scroll-area.js +13 -0
  93. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  94. package/lib/chat/components/ui/separator.js +21 -0
  95. package/lib/chat/components/ui/separator.jsx +18 -0
  96. package/lib/chat/components/ui/sheet.js +75 -0
  97. package/lib/chat/components/ui/sheet.jsx +95 -0
  98. package/lib/chat/components/ui/sidebar.js +228 -0
  99. package/lib/chat/components/ui/sidebar.jsx +246 -0
  100. package/lib/chat/components/ui/tooltip.js +56 -0
  101. package/lib/chat/components/ui/tooltip.jsx +66 -0
  102. package/lib/chat/components/upgrade-dialog.js +151 -0
  103. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  104. package/lib/chat/utils.js +11 -0
  105. package/lib/code/actions.js +153 -0
  106. package/lib/code/code-page.js +22 -0
  107. package/lib/code/code-page.jsx +25 -0
  108. package/lib/code/index.js +1 -0
  109. package/lib/code/terminal-view.js +201 -0
  110. package/lib/code/terminal-view.jsx +224 -0
  111. package/lib/code/ws-proxy.js +80 -0
  112. package/lib/cron.js +246 -0
  113. package/lib/db/api-keys.js +163 -0
  114. package/lib/db/chats.js +168 -0
  115. package/lib/db/code-workspaces.js +110 -0
  116. package/lib/db/index.js +52 -0
  117. package/lib/db/notifications.js +99 -0
  118. package/lib/db/schema.js +66 -0
  119. package/lib/db/update-check.js +96 -0
  120. package/lib/db/users.js +89 -0
  121. package/lib/paths.js +42 -0
  122. package/lib/tools/create-job.js +97 -0
  123. package/lib/tools/docker.js +146 -0
  124. package/lib/tools/github.js +271 -0
  125. package/lib/tools/openai.js +35 -0
  126. package/lib/tools/telegram.js +292 -0
  127. package/lib/triggers.js +104 -0
  128. package/lib/utils/render-md.js +111 -0
  129. package/package.json +118 -0
  130. package/setup/lib/auth.mjs +81 -0
  131. package/setup/lib/env.mjs +21 -0
  132. package/setup/lib/fs-utils.mjs +20 -0
  133. package/setup/lib/github.mjs +149 -0
  134. package/setup/lib/prerequisites.mjs +155 -0
  135. package/setup/lib/prompts.mjs +267 -0
  136. package/setup/lib/providers.mjs +105 -0
  137. package/setup/lib/sync.mjs +125 -0
  138. package/setup/lib/targets.mjs +45 -0
  139. package/setup/lib/telegram-verify.mjs +63 -0
  140. package/setup/lib/telegram.mjs +76 -0
  141. package/setup/setup-cloud.mjs +833 -0
  142. package/setup/setup-local.mjs +377 -0
  143. package/setup/setup-telegram.mjs +265 -0
  144. package/setup/setup.mjs +87 -0
  145. package/templates/.dockerignore +5 -0
  146. package/templates/.env.example +104 -0
  147. package/templates/.github/workflows/auto-merge.yml +117 -0
  148. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  149. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  150. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  151. package/templates/.github/workflows/run-job.yml +89 -0
  152. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  153. package/templates/.gitignore.template +45 -0
  154. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  155. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  156. package/templates/CLAUDE.md +29 -0
  157. package/templates/CLAUDE.md.template +308 -0
  158. package/templates/app/api/[...gigaclaw]/route.js +1 -0
  159. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  160. package/templates/app/chat/[chatId]/page.js +9 -0
  161. package/templates/app/chats/page.js +7 -0
  162. package/templates/app/code/[codeWorkspaceId]/page.js +9 -0
  163. package/templates/app/components/ascii-logo.jsx +12 -0
  164. package/templates/app/components/login-form.jsx +92 -0
  165. package/templates/app/components/setup-form.jsx +82 -0
  166. package/templates/app/components/theme-provider.jsx +11 -0
  167. package/templates/app/components/theme-toggle.jsx +38 -0
  168. package/templates/app/components/ui/button.jsx +21 -0
  169. package/templates/app/components/ui/card.jsx +23 -0
  170. package/templates/app/components/ui/input.jsx +10 -0
  171. package/templates/app/components/ui/label.jsx +10 -0
  172. package/templates/app/crons/page.js +5 -0
  173. package/templates/app/globals.css +90 -0
  174. package/templates/app/layout.js +33 -0
  175. package/templates/app/login/page.js +15 -0
  176. package/templates/app/notifications/page.js +7 -0
  177. package/templates/app/page.js +7 -0
  178. package/templates/app/pull-requests/page.js +7 -0
  179. package/templates/app/settings/crons/page.js +5 -0
  180. package/templates/app/settings/layout.js +7 -0
  181. package/templates/app/settings/page.js +5 -0
  182. package/templates/app/settings/secrets/page.js +5 -0
  183. package/templates/app/settings/triggers/page.js +5 -0
  184. package/templates/app/stream/chat/route.js +1 -0
  185. package/templates/app/swarm/page.js +7 -0
  186. package/templates/app/triggers/page.js +5 -0
  187. package/templates/config/CODE_PLANNING.md +14 -0
  188. package/templates/config/CRONS.json +56 -0
  189. package/templates/config/HEARTBEAT.md +3 -0
  190. package/templates/config/JOB_AGENT.md +30 -0
  191. package/templates/config/JOB_PLANNING.md +240 -0
  192. package/templates/config/JOB_SUMMARY.md +130 -0
  193. package/templates/config/SKILL_BUILDING_GUIDE.md +96 -0
  194. package/templates/config/SOUL.md +48 -0
  195. package/templates/config/TRIGGERS.json +58 -0
  196. package/templates/config/WEB_SEARCH_AVAILABLE.md +5 -0
  197. package/templates/config/WEB_SEARCH_UNAVAILABLE.md +3 -0
  198. package/templates/docker/claude-code-job/Dockerfile +34 -0
  199. package/templates/docker/claude-code-job/entrypoint.sh +149 -0
  200. package/templates/docker/claude-code-workspace/.tmux.conf +5 -0
  201. package/templates/docker/claude-code-workspace/Dockerfile +61 -0
  202. package/templates/docker/claude-code-workspace/entrypoint.sh +51 -0
  203. package/templates/docker/event-handler/Dockerfile +20 -0
  204. package/templates/docker/event-handler/ecosystem.config.cjs +7 -0
  205. package/templates/docker/pi-coding-agent-job/Dockerfile +51 -0
  206. package/templates/docker/pi-coding-agent-job/entrypoint.sh +164 -0
  207. package/templates/docker-compose.local.yml +78 -0
  208. package/templates/docker-compose.yml +64 -0
  209. package/templates/instrumentation.js +6 -0
  210. package/templates/middleware.js +23 -0
  211. package/templates/next.config.mjs +3 -0
  212. package/templates/postcss.config.mjs +5 -0
  213. package/templates/public/favicon.ico +0 -0
  214. package/templates/server.js +25 -0
  215. package/templates/skills/LICENSE +21 -0
  216. package/templates/skills/README.md +119 -0
  217. package/templates/skills/brave-search/SKILL.md +79 -0
  218. package/templates/skills/brave-search/content.js +86 -0
  219. package/templates/skills/brave-search/package-lock.json +621 -0
  220. package/templates/skills/brave-search/package.json +14 -0
  221. package/templates/skills/brave-search/search.js +199 -0
  222. package/templates/skills/browser-tools/SKILL.md +196 -0
  223. package/templates/skills/browser-tools/browser-content.js +103 -0
  224. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  225. package/templates/skills/browser-tools/browser-eval.js +53 -0
  226. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  227. package/templates/skills/browser-tools/browser-nav.js +44 -0
  228. package/templates/skills/browser-tools/browser-pick.js +162 -0
  229. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  230. package/templates/skills/browser-tools/browser-start.js +87 -0
  231. package/templates/skills/browser-tools/package-lock.json +2556 -0
  232. package/templates/skills/browser-tools/package.json +19 -0
  233. package/templates/skills/google-docs/SKILL.md +23 -0
  234. package/templates/skills/google-docs/create.sh +69 -0
  235. package/templates/skills/google-drive/SKILL.md +47 -0
  236. package/templates/skills/google-drive/delete.sh +47 -0
  237. package/templates/skills/google-drive/download.sh +50 -0
  238. package/templates/skills/google-drive/list.sh +41 -0
  239. package/templates/skills/google-drive/upload.sh +76 -0
  240. package/templates/skills/kie-ai/SKILL.md +38 -0
  241. package/templates/skills/kie-ai/generate-image.sh +77 -0
  242. package/templates/skills/kie-ai/generate-video.sh +69 -0
  243. package/templates/skills/llm-secrets/SKILL.md +34 -0
  244. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  245. package/templates/skills/modify-self/SKILL.md +12 -0
  246. package/templates/skills/youtube-transcript/SKILL.md +41 -0
  247. package/templates/skills/youtube-transcript/package-lock.json +24 -0
  248. package/templates/skills/youtube-transcript/package.json +8 -0
  249. package/templates/skills/youtube-transcript/transcript.js +84 -0
@@ -0,0 +1,194 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { createContext, useContext, useState, useEffect, useRef } from "react";
4
+ import { cn } from "../../utils.js";
5
+ const DropdownContext = createContext({ open: false, onOpenChange: () => {
6
+ } });
7
+ function DropdownMenu({ children, open: controlledOpen, onOpenChange: controlledOnOpenChange }) {
8
+ const [internalOpen, setInternalOpen] = useState(false);
9
+ const open = controlledOpen !== void 0 ? controlledOpen : internalOpen;
10
+ const onOpenChange = controlledOnOpenChange || setInternalOpen;
11
+ return /* @__PURE__ */ jsx(DropdownContext.Provider, { value: { open, onOpenChange }, children: /* @__PURE__ */ jsx("div", { className: "relative inline-block", children }) });
12
+ }
13
+ function DropdownMenuTrigger({ children, asChild, ...props }) {
14
+ const { open, onOpenChange } = useContext(DropdownContext);
15
+ const handleClick = (e) => {
16
+ e.stopPropagation();
17
+ onOpenChange(!open);
18
+ };
19
+ const child = Array.isArray(children) ? children[0] : children;
20
+ const childIsInteractive = child && typeof child === "object" && "type" in child && (child.type === "button" || child.type === "a");
21
+ if ((asChild || childIsInteractive) && child && typeof child === "object" && "props" in child) {
22
+ return /* @__PURE__ */ jsx(
23
+ child.type,
24
+ {
25
+ ...child.props,
26
+ onClick: (e) => {
27
+ child.props.onClick?.(e);
28
+ handleClick(e);
29
+ },
30
+ "aria-expanded": open,
31
+ "data-state": open ? "open" : "closed"
32
+ }
33
+ );
34
+ }
35
+ return /* @__PURE__ */ jsx(
36
+ "span",
37
+ {
38
+ role: "button",
39
+ tabIndex: 0,
40
+ onClick: handleClick,
41
+ onKeyDown: (e) => {
42
+ if (e.key === "Enter" || e.key === " ") handleClick(e);
43
+ },
44
+ "aria-expanded": open,
45
+ "data-state": open ? "open" : "closed",
46
+ ...props,
47
+ children
48
+ }
49
+ );
50
+ }
51
+ function DropdownMenuContent({ children, className, align = "start", side = "bottom", sideOffset = 4, ...props }) {
52
+ const { open, onOpenChange } = useContext(DropdownContext);
53
+ const ref = useRef(null);
54
+ useEffect(() => {
55
+ if (!open) return;
56
+ const handleClickOutside = (e) => {
57
+ if (ref.current && !ref.current.contains(e.target)) {
58
+ onOpenChange(false);
59
+ }
60
+ };
61
+ const handleEsc = (e) => {
62
+ if (e.key === "Escape") onOpenChange(false);
63
+ };
64
+ setTimeout(() => document.addEventListener("click", handleClickOutside), 0);
65
+ document.addEventListener("keydown", handleEsc);
66
+ return () => {
67
+ document.removeEventListener("click", handleClickOutside);
68
+ document.removeEventListener("keydown", handleEsc);
69
+ };
70
+ }, [open, onOpenChange]);
71
+ if (!open) return null;
72
+ return /* @__PURE__ */ jsx(
73
+ "div",
74
+ {
75
+ ref,
76
+ className: cn(
77
+ "absolute z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-background/80 backdrop-blur-sm p-1 text-foreground shadow-lg",
78
+ side === "bottom" && `top-full mt-1`,
79
+ side === "top" && `bottom-full mb-1`,
80
+ align === "end" && "right-0",
81
+ align === "start" && "left-0",
82
+ className
83
+ ),
84
+ ...props,
85
+ children
86
+ }
87
+ );
88
+ }
89
+ function DropdownMenuItem({ children, className, onClick, asChild, ...props }) {
90
+ const { onOpenChange } = useContext(DropdownContext);
91
+ const handleClick = (e) => {
92
+ onClick?.(e);
93
+ onOpenChange(false);
94
+ };
95
+ if (asChild && children) {
96
+ const child = Array.isArray(children) ? children[0] : children;
97
+ if (child && typeof child === "object" && "props" in child) {
98
+ return /* @__PURE__ */ jsx(
99
+ child.type,
100
+ {
101
+ ...child.props,
102
+ className: cn(
103
+ "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background",
104
+ child.props.className,
105
+ className
106
+ ),
107
+ onClick: (e) => {
108
+ child.props.onClick?.(e);
109
+ handleClick(e);
110
+ },
111
+ role: "menuitem"
112
+ }
113
+ );
114
+ }
115
+ }
116
+ return /* @__PURE__ */ jsx(
117
+ "div",
118
+ {
119
+ role: "menuitem",
120
+ className: cn(
121
+ "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background",
122
+ className
123
+ ),
124
+ onClick: handleClick,
125
+ ...props,
126
+ children
127
+ }
128
+ );
129
+ }
130
+ function DropdownMenuSeparator({ className }) {
131
+ return /* @__PURE__ */ jsx("div", { className: cn("-mx-1 my-1 h-px bg-border", className) });
132
+ }
133
+ function DropdownMenuLabel({ children, className }) {
134
+ return /* @__PURE__ */ jsx("div", { className: cn("px-2 py-1.5 text-sm font-semibold", className), children });
135
+ }
136
+ function DropdownMenuGroup({ children }) {
137
+ return /* @__PURE__ */ jsx("div", { children });
138
+ }
139
+ const SubContext = createContext({ open: false, setOpen: () => {
140
+ } });
141
+ function DropdownMenuSub({ children }) {
142
+ const [open, setOpen] = useState(false);
143
+ return /* @__PURE__ */ jsx(SubContext.Provider, { value: { open, setOpen }, children: /* @__PURE__ */ jsx("div", { className: "relative", children }) });
144
+ }
145
+ function DropdownMenuSubTrigger({ children, className }) {
146
+ const { open, setOpen } = useContext(SubContext);
147
+ return /* @__PURE__ */ jsxs(
148
+ "div",
149
+ {
150
+ role: "menuitem",
151
+ "aria-haspopup": "menu",
152
+ "aria-expanded": open,
153
+ className: cn(
154
+ "relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background justify-between",
155
+ className
156
+ ),
157
+ onClick: (e) => {
158
+ e.stopPropagation();
159
+ setOpen((v) => !v);
160
+ },
161
+ children: [
162
+ /* @__PURE__ */ jsx("span", { className: "flex items-center gap-2", children }),
163
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" }) })
164
+ ]
165
+ }
166
+ );
167
+ }
168
+ function DropdownMenuSubContent({ children, className }) {
169
+ const { open } = useContext(SubContext);
170
+ if (!open) return null;
171
+ return /* @__PURE__ */ jsx(
172
+ "div",
173
+ {
174
+ role: "menu",
175
+ className: cn(
176
+ "absolute left-full top-0 z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-background/80 backdrop-blur-sm p-1 text-foreground shadow-lg ml-1",
177
+ className
178
+ ),
179
+ children
180
+ }
181
+ );
182
+ }
183
+ export {
184
+ DropdownMenu,
185
+ DropdownMenuContent,
186
+ DropdownMenuGroup,
187
+ DropdownMenuItem,
188
+ DropdownMenuLabel,
189
+ DropdownMenuSeparator,
190
+ DropdownMenuSub,
191
+ DropdownMenuSubContent,
192
+ DropdownMenuSubTrigger,
193
+ DropdownMenuTrigger
194
+ };
@@ -0,0 +1,215 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, useState, useEffect, useRef } from 'react';
4
+ import { cn } from '../../utils.js';
5
+
6
+ const DropdownContext = createContext({ open: false, onOpenChange: () => {} });
7
+
8
+ export function DropdownMenu({ children, open: controlledOpen, onOpenChange: controlledOnOpenChange }) {
9
+ const [internalOpen, setInternalOpen] = useState(false);
10
+ const open = controlledOpen !== undefined ? controlledOpen : internalOpen;
11
+ const onOpenChange = controlledOnOpenChange || setInternalOpen;
12
+
13
+ return (
14
+ <DropdownContext.Provider value={{ open, onOpenChange }}>
15
+ <div className="relative inline-block">{children}</div>
16
+ </DropdownContext.Provider>
17
+ );
18
+ }
19
+
20
+ export function DropdownMenuTrigger({ children, asChild, ...props }) {
21
+ const { open, onOpenChange } = useContext(DropdownContext);
22
+ const handleClick = (e) => {
23
+ e.stopPropagation();
24
+ onOpenChange(!open);
25
+ };
26
+
27
+ // Slot pattern: when asChild=true (or when the child is already a button/a),
28
+ // clone the child and merge in our handler instead of wrapping in another element.
29
+ // This prevents <button><button> nesting which causes React hydration errors.
30
+ const child = Array.isArray(children) ? children[0] : children;
31
+ const childIsInteractive =
32
+ child && typeof child === 'object' && 'type' in child &&
33
+ (child.type === 'button' || child.type === 'a');
34
+
35
+ if ((asChild || childIsInteractive) && child && typeof child === 'object' && 'props' in child) {
36
+ return (
37
+ <child.type
38
+ {...child.props}
39
+ onClick={(e) => {
40
+ child.props.onClick?.(e);
41
+ handleClick(e);
42
+ }}
43
+ aria-expanded={open}
44
+ data-state={open ? 'open' : 'closed'}
45
+ />
46
+ );
47
+ }
48
+
49
+ // Default: render a semantically neutral <span> (not <button>) so callers
50
+ // that pass <button> children never produce invalid <button><button> nesting.
51
+ return (
52
+ <span
53
+ role="button"
54
+ tabIndex={0}
55
+ onClick={handleClick}
56
+ onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') handleClick(e); }}
57
+ aria-expanded={open}
58
+ data-state={open ? 'open' : 'closed'}
59
+ {...props}
60
+ >
61
+ {children}
62
+ </span>
63
+ );
64
+ }
65
+
66
+ export function DropdownMenuContent({ children, className, align = 'start', side = 'bottom', sideOffset = 4, ...props }) {
67
+ const { open, onOpenChange } = useContext(DropdownContext);
68
+ const ref = useRef(null);
69
+
70
+ useEffect(() => {
71
+ if (!open) return;
72
+ const handleClickOutside = (e) => {
73
+ if (ref.current && !ref.current.contains(e.target)) {
74
+ onOpenChange(false);
75
+ }
76
+ };
77
+ const handleEsc = (e) => {
78
+ if (e.key === 'Escape') onOpenChange(false);
79
+ };
80
+ setTimeout(() => document.addEventListener('click', handleClickOutside), 0);
81
+ document.addEventListener('keydown', handleEsc);
82
+ return () => {
83
+ document.removeEventListener('click', handleClickOutside);
84
+ document.removeEventListener('keydown', handleEsc);
85
+ };
86
+ }, [open, onOpenChange]);
87
+
88
+ if (!open) return null;
89
+
90
+ return (
91
+ <div
92
+ ref={ref}
93
+ className={cn(
94
+ 'absolute z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-background/80 backdrop-blur-sm p-1 text-foreground shadow-lg',
95
+ side === 'bottom' && `top-full mt-1`,
96
+ side === 'top' && `bottom-full mb-1`,
97
+ align === 'end' && 'right-0',
98
+ align === 'start' && 'left-0',
99
+ className
100
+ )}
101
+ {...props}
102
+ >
103
+ {children}
104
+ </div>
105
+ );
106
+ }
107
+
108
+ export function DropdownMenuItem({ children, className, onClick, asChild, ...props }) {
109
+ const { onOpenChange } = useContext(DropdownContext);
110
+ const handleClick = (e) => {
111
+ onClick?.(e);
112
+ onOpenChange(false);
113
+ };
114
+
115
+ // When asChild=true, clone the single child element and merge in our
116
+ // click handler + className instead of wrapping in a <div>.
117
+ // This prevents React from seeing `asChild` as an unknown DOM attribute.
118
+ if (asChild && children) {
119
+ const child = Array.isArray(children) ? children[0] : children;
120
+ if (child && typeof child === 'object' && 'props' in child) {
121
+ return (
122
+ <child.type
123
+ {...child.props}
124
+ className={cn(
125
+ 'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background',
126
+ child.props.className,
127
+ className
128
+ )}
129
+ onClick={(e) => {
130
+ child.props.onClick?.(e);
131
+ handleClick(e);
132
+ }}
133
+ role="menuitem"
134
+ />
135
+ );
136
+ }
137
+ }
138
+
139
+ return (
140
+ <div
141
+ role="menuitem"
142
+ className={cn(
143
+ 'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background',
144
+ className
145
+ )}
146
+ onClick={handleClick}
147
+ {...props}
148
+ >
149
+ {children}
150
+ </div>
151
+ );
152
+ }
153
+
154
+ export function DropdownMenuSeparator({ className }) {
155
+ return <div className={cn('-mx-1 my-1 h-px bg-border', className)} />;
156
+ }
157
+
158
+ export function DropdownMenuLabel({ children, className }) {
159
+ return (
160
+ <div className={cn('px-2 py-1.5 text-sm font-semibold', className)}>
161
+ {children}
162
+ </div>
163
+ );
164
+ }
165
+
166
+ export function DropdownMenuGroup({ children }) {
167
+ return <div>{children}</div>;
168
+ }
169
+
170
+ // ─── Submenu ─────────────────────────────────────────────────────────────────
171
+ const SubContext = createContext({ open: false, setOpen: () => {} });
172
+
173
+ export function DropdownMenuSub({ children }) {
174
+ const [open, setOpen] = useState(false);
175
+ return (
176
+ <SubContext.Provider value={{ open, setOpen }}>
177
+ <div className="relative">{children}</div>
178
+ </SubContext.Provider>
179
+ );
180
+ }
181
+
182
+ export function DropdownMenuSubTrigger({ children, className }) {
183
+ const { open, setOpen } = useContext(SubContext);
184
+ return (
185
+ <div
186
+ role="menuitem"
187
+ aria-haspopup="menu"
188
+ aria-expanded={open}
189
+ className={cn(
190
+ 'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-background focus:bg-background justify-between',
191
+ className
192
+ )}
193
+ onClick={(e) => { e.stopPropagation(); setOpen((v) => !v); }}
194
+ >
195
+ <span className="flex items-center gap-2">{children}</span>
196
+ <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6" /></svg>
197
+ </div>
198
+ );
199
+ }
200
+
201
+ export function DropdownMenuSubContent({ children, className }) {
202
+ const { open } = useContext(SubContext);
203
+ if (!open) return null;
204
+ return (
205
+ <div
206
+ role="menu"
207
+ className={cn(
208
+ 'absolute left-full top-0 z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-background/80 backdrop-blur-sm p-1 text-foreground shadow-lg ml-1',
209
+ className
210
+ )}
211
+ >
212
+ {children}
213
+ </div>
214
+ );
215
+ }
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect, useRef } from "react";
4
+ import { createPortal } from "react-dom";
5
+ function RenameDialog({ open, onSave, onCancel, title = "Rename chat", currentValue = "" }) {
6
+ const [value, setValue] = useState(currentValue);
7
+ const inputRef = useRef(null);
8
+ useEffect(() => {
9
+ if (open) {
10
+ setValue(currentValue);
11
+ setTimeout(() => {
12
+ if (inputRef.current) {
13
+ inputRef.current.focus();
14
+ inputRef.current.select();
15
+ }
16
+ }, 0);
17
+ }
18
+ }, [open, currentValue]);
19
+ useEffect(() => {
20
+ if (!open) return;
21
+ const handleEsc = (e) => {
22
+ if (e.key === "Escape") onCancel();
23
+ };
24
+ document.addEventListener("keydown", handleEsc);
25
+ return () => document.removeEventListener("keydown", handleEsc);
26
+ }, [open, onCancel]);
27
+ const handleSave = () => {
28
+ const trimmed = value.trim();
29
+ if (trimmed && trimmed !== currentValue) {
30
+ onSave(trimmed);
31
+ }
32
+ onCancel();
33
+ };
34
+ if (!open) return null;
35
+ return createPortal(
36
+ /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
37
+ /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/50", onClick: onCancel }),
38
+ /* @__PURE__ */ jsxs("div", { className: "relative z-50 w-full max-w-sm rounded-lg border border-border bg-background p-6 shadow-lg", onClick: (e) => e.stopPropagation(), children: [
39
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: title }),
40
+ /* @__PURE__ */ jsx(
41
+ "input",
42
+ {
43
+ ref: inputRef,
44
+ type: "text",
45
+ value,
46
+ onChange: (e) => setValue(e.target.value),
47
+ onKeyDown: (e) => {
48
+ if (e.key === "Enter") handleSave();
49
+ },
50
+ className: "mt-3 w-full rounded-md border border-input bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
51
+ }
52
+ ),
53
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [
54
+ /* @__PURE__ */ jsx(
55
+ "button",
56
+ {
57
+ onClick: onCancel,
58
+ className: "rounded-md px-3 py-1.5 text-sm font-medium border border-input bg-background hover:bg-muted",
59
+ children: "Cancel"
60
+ }
61
+ ),
62
+ /* @__PURE__ */ jsx(
63
+ "button",
64
+ {
65
+ onClick: handleSave,
66
+ className: "rounded-md px-3 py-1.5 text-sm font-medium text-white bg-foreground hover:bg-foreground/90",
67
+ children: "Save"
68
+ }
69
+ )
70
+ ] })
71
+ ] })
72
+ ] }),
73
+ document.body
74
+ );
75
+ }
76
+ export {
77
+ RenameDialog
78
+ };
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useRef } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+
6
+ export function RenameDialog({ open, onSave, onCancel, title = 'Rename chat', currentValue = '' }) {
7
+ const [value, setValue] = useState(currentValue);
8
+ const inputRef = useRef(null);
9
+
10
+ useEffect(() => {
11
+ if (open) {
12
+ setValue(currentValue);
13
+ setTimeout(() => {
14
+ if (inputRef.current) {
15
+ inputRef.current.focus();
16
+ inputRef.current.select();
17
+ }
18
+ }, 0);
19
+ }
20
+ }, [open, currentValue]);
21
+
22
+ useEffect(() => {
23
+ if (!open) return;
24
+ const handleEsc = (e) => {
25
+ if (e.key === 'Escape') onCancel();
26
+ };
27
+ document.addEventListener('keydown', handleEsc);
28
+ return () => document.removeEventListener('keydown', handleEsc);
29
+ }, [open, onCancel]);
30
+
31
+ const handleSave = () => {
32
+ const trimmed = value.trim();
33
+ if (trimmed && trimmed !== currentValue) {
34
+ onSave(trimmed);
35
+ }
36
+ onCancel();
37
+ };
38
+
39
+ if (!open) return null;
40
+
41
+ return createPortal(
42
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
43
+ <div className="fixed inset-0 bg-black/50" onClick={onCancel} />
44
+ <div className="relative z-50 w-full max-w-sm rounded-lg border border-border bg-background p-6 shadow-lg" onClick={(e) => e.stopPropagation()}>
45
+ <h3 className="text-lg font-semibold">{title}</h3>
46
+ <input
47
+ ref={inputRef}
48
+ type="text"
49
+ value={value}
50
+ onChange={(e) => setValue(e.target.value)}
51
+ onKeyDown={(e) => {
52
+ if (e.key === 'Enter') handleSave();
53
+ }}
54
+ className="mt-3 w-full rounded-md border border-input bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
55
+ />
56
+ <div className="mt-4 flex justify-end gap-2">
57
+ <button
58
+ onClick={onCancel}
59
+ className="rounded-md px-3 py-1.5 text-sm font-medium border border-input bg-background hover:bg-muted"
60
+ >
61
+ Cancel
62
+ </button>
63
+ <button
64
+ onClick={handleSave}
65
+ className="rounded-md px-3 py-1.5 text-sm font-medium text-white bg-foreground hover:bg-foreground/90"
66
+ >
67
+ Save
68
+ </button>
69
+ </div>
70
+ </div>
71
+ </div>,
72
+ document.body
73
+ );
74
+ }
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { cn } from "../../utils.js";
4
+ function ScrollArea({ children, className, ...props }) {
5
+ return /* @__PURE__ */ jsx("div", { className: cn("relative overflow-hidden", className), ...props, children: /* @__PURE__ */ jsx("div", { className: "h-full w-full overflow-y-auto scrollbar-thin", children }) });
6
+ }
7
+ function ScrollBar() {
8
+ return null;
9
+ }
10
+ export {
11
+ ScrollArea,
12
+ ScrollBar
13
+ };
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+
3
+ import { cn } from '../../utils.js';
4
+
5
+ export function ScrollArea({ children, className, ...props }) {
6
+ return (
7
+ <div className={cn('relative overflow-hidden', className)} {...props}>
8
+ <div className="h-full w-full overflow-y-auto scrollbar-thin">
9
+ {children}
10
+ </div>
11
+ </div>
12
+ );
13
+ }
14
+
15
+ export function ScrollBar() {
16
+ return null;
17
+ }
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { cn } from "../../utils.js";
4
+ function Separator({ className, orientation = "horizontal", ...props }) {
5
+ return /* @__PURE__ */ jsx(
6
+ "div",
7
+ {
8
+ role: "separator",
9
+ "aria-orientation": orientation,
10
+ className: cn(
11
+ "shrink-0 bg-border",
12
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
13
+ className
14
+ ),
15
+ ...props
16
+ }
17
+ );
18
+ }
19
+ export {
20
+ Separator
21
+ };
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+
3
+ import { cn } from '../../utils.js';
4
+
5
+ export function Separator({ className, orientation = 'horizontal', ...props }) {
6
+ return (
7
+ <div
8
+ role="separator"
9
+ aria-orientation={orientation}
10
+ className={cn(
11
+ 'shrink-0 bg-border',
12
+ orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ );
18
+ }