kcg-code 0.0.0-stage → 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 (307) hide show
  1. package/README.md +230 -2
  2. package/bin/kcgcode.ts +13 -0
  3. package/bun-env.d.ts +19 -0
  4. package/kcg-code.config.example.json +3 -0
  5. package/package.json +84 -4
  6. package/public/logo.svg +1 -0
  7. package/public/manifest.json +18 -0
  8. package/public/sw.js +49 -0
  9. package/src/App.tsx +39 -0
  10. package/src/__tests__/manifest.test.ts +61 -0
  11. package/src/build-config.ts +9 -0
  12. package/src/cli/__tests__/daemon.test.ts +84 -0
  13. package/src/cli/__tests__/dashboard.test.ts +224 -0
  14. package/src/cli/__tests__/keep-awake.test.ts +44 -0
  15. package/src/cli/__tests__/port.test.ts +86 -0
  16. package/src/cli/args.ts +186 -0
  17. package/src/cli/attach.ts +154 -0
  18. package/src/cli/commands.ts +327 -0
  19. package/src/cli/daemon.ts +275 -0
  20. package/src/cli/dashboard.tsx +1060 -0
  21. package/src/cli/index.ts +47 -0
  22. package/src/cli/keep-awake.ts +148 -0
  23. package/src/cli/port.ts +92 -0
  24. package/src/cli/theme.ts +100 -0
  25. package/src/components/auth/AuthGate.tsx +182 -0
  26. package/src/components/auth/AvatarEditor.tsx +236 -0
  27. package/src/components/auth/LockScreen.tsx +714 -0
  28. package/src/components/auth/ProfileAvatar.tsx +60 -0
  29. package/src/components/auth/WelcomeScreen.tsx +672 -0
  30. package/src/components/auth/__tests__/WelcomeScreen.test.tsx +42 -0
  31. package/src/components/auth/avatar-icons.tsx +114 -0
  32. package/src/components/layout/AppShell.tsx +147 -0
  33. package/src/components/layout/AppSidebar.tsx +419 -0
  34. package/src/components/layout/SearchDialog.tsx +189 -0
  35. package/src/components/layout/SidebarAccount.tsx +111 -0
  36. package/src/components/layout/SidebarSessionMenu.tsx +163 -0
  37. package/src/components/layout/ThemeToggle.tsx +67 -0
  38. package/src/components/projects/FolderBrowser.tsx +142 -0
  39. package/src/components/projects/HomeChat.tsx +287 -0
  40. package/src/components/projects/HomeRecentSessions.tsx +90 -0
  41. package/src/components/projects/NewProjectDialog.tsx +249 -0
  42. package/src/components/projects/ProjectInstructions.tsx +218 -0
  43. package/src/components/projects/ProjectRow.tsx +58 -0
  44. package/src/components/projects/ProjectSidePanel.tsx +514 -0
  45. package/src/components/sessions/AgentLogo.tsx +42 -0
  46. package/src/components/sessions/AgentModeList.tsx +265 -0
  47. package/src/components/sessions/AgentPicker.tsx +132 -0
  48. package/src/components/sessions/AssistantTurn.tsx +145 -0
  49. package/src/components/sessions/AttachmentTile.tsx +161 -0
  50. package/src/components/sessions/CodeBlock.tsx +107 -0
  51. package/src/components/sessions/ComposerActionSheet.tsx +96 -0
  52. package/src/components/sessions/ConfirmDeleteProjectDialog.tsx +80 -0
  53. package/src/components/sessions/ConfirmSessionDeleteDialog.tsx +92 -0
  54. package/src/components/sessions/ContextPanel.tsx +661 -0
  55. package/src/components/sessions/DropPlate.tsx +165 -0
  56. package/src/components/sessions/FileDropOverlay.tsx +232 -0
  57. package/src/components/sessions/FileMentionDropdown.tsx +47 -0
  58. package/src/components/sessions/ImageLightbox.tsx +173 -0
  59. package/src/components/sessions/MarkdownContent.tsx +187 -0
  60. package/src/components/sessions/Mascot.tsx +47 -0
  61. package/src/components/sessions/MessageActions.tsx +60 -0
  62. package/src/components/sessions/MessageRail.tsx +121 -0
  63. package/src/components/sessions/ModelPicker.tsx +247 -0
  64. package/src/components/sessions/ModelSearchList.tsx +231 -0
  65. package/src/components/sessions/NewSessionDialog.tsx +172 -0
  66. package/src/components/sessions/PendingAttachments.tsx +127 -0
  67. package/src/components/sessions/PromptCard.tsx +1085 -0
  68. package/src/components/sessions/PromptPanel.tsx +112 -0
  69. package/src/components/sessions/SessionComposer.tsx +641 -0
  70. package/src/components/sessions/SessionHeader.tsx +176 -0
  71. package/src/components/sessions/SessionList.tsx +503 -0
  72. package/src/components/sessions/SessionRow.tsx +237 -0
  73. package/src/components/sessions/SessionTimeline.tsx +275 -0
  74. package/src/components/sessions/SessionTimelineSkeleton.tsx +83 -0
  75. package/src/components/sessions/SessionView.tsx +240 -0
  76. package/src/components/sessions/ThoughtProcessBlock.tsx +199 -0
  77. package/src/components/sessions/UserMessage.tsx +177 -0
  78. package/src/components/sessions/__tests__/ContextPanel.test.tsx +256 -0
  79. package/src/components/sessions/__tests__/MarkdownContent.test.tsx +89 -0
  80. package/src/components/sessions/__tests__/MessageRail.test.tsx +40 -0
  81. package/src/components/sessions/__tests__/PermissionCard.test.tsx +79 -0
  82. package/src/components/sessions/__tests__/PromptCard.test.ts +166 -0
  83. package/src/components/sessions/__tests__/QuestionCard.test.tsx +166 -0
  84. package/src/components/sessions/__tests__/SessionTimelineSkeleton.test.tsx +32 -0
  85. package/src/components/sessions/__tests__/SessionView.test.ts +386 -0
  86. package/src/components/settings/AboutGroup.tsx +170 -0
  87. package/src/components/settings/AccountGroup.tsx +122 -0
  88. package/src/components/settings/DevicesGroup.tsx +151 -0
  89. package/src/components/settings/LhrRemoteAccess.tsx +257 -0
  90. package/src/components/settings/PasskeysGroup.tsx +551 -0
  91. package/src/components/settings/RemoteAccessGroup.tsx +1201 -0
  92. package/src/components/settings/SecurityGroup.tsx +566 -0
  93. package/src/components/settings/WorkspaceGroup.tsx +370 -0
  94. package/src/components/settings/__tests__/AboutGroup.test.tsx +47 -0
  95. package/src/components/settings/__tests__/PasskeysGroup.test.tsx +109 -0
  96. package/src/components/settings/__tests__/WorkspaceGroup.test.tsx +49 -0
  97. package/src/components/settings/prefs.tsx +333 -0
  98. package/src/components/skills/InstallNotifications.tsx +326 -0
  99. package/src/components/skills/InstallSkillDialog.tsx +390 -0
  100. package/src/components/skills/SkillCards.tsx +254 -0
  101. package/src/components/skills/SkillsProjectPicker.tsx +178 -0
  102. package/src/components/ui/adw-dialog.tsx +114 -0
  103. package/src/components/ui/adw-toggle-group.tsx +75 -0
  104. package/src/components/ui/alert-dialog.tsx +179 -0
  105. package/src/components/ui/alert.tsx +60 -0
  106. package/src/components/ui/badge.tsx +46 -0
  107. package/src/components/ui/bubble.tsx +125 -0
  108. package/src/components/ui/button.tsx +62 -0
  109. package/src/components/ui/card.tsx +75 -0
  110. package/src/components/ui/collapsible.tsx +19 -0
  111. package/src/components/ui/dialog.tsx +142 -0
  112. package/src/components/ui/dropdown-menu.tsx +225 -0
  113. package/src/components/ui/empty.tsx +94 -0
  114. package/src/components/ui/field.tsx +234 -0
  115. package/src/components/ui/input-group.tsx +161 -0
  116. package/src/components/ui/input.tsx +21 -0
  117. package/src/components/ui/label.tsx +19 -0
  118. package/src/components/ui/message-scroller.tsx +131 -0
  119. package/src/components/ui/message.tsx +85 -0
  120. package/src/components/ui/popover.tsx +46 -0
  121. package/src/components/ui/search-entry.tsx +70 -0
  122. package/src/components/ui/select.tsx +172 -0
  123. package/src/components/ui/separator.tsx +28 -0
  124. package/src/components/ui/sheet.tsx +138 -0
  125. package/src/components/ui/sidebar.tsx +699 -0
  126. package/src/components/ui/skeleton.tsx +15 -0
  127. package/src/components/ui/sonner.tsx +45 -0
  128. package/src/components/ui/spinner.tsx +16 -0
  129. package/src/components/ui/switch.tsx +32 -0
  130. package/src/components/ui/textarea.tsx +18 -0
  131. package/src/components/ui/tooltip.tsx +51 -0
  132. package/src/config.ts +114 -0
  133. package/src/db.ts +146 -0
  134. package/src/frontend.tsx +51 -0
  135. package/src/hooks/__tests__/useMention.test.ts +79 -0
  136. package/src/hooks/__tests__/useResyncOnReconnect.test.ts +128 -0
  137. package/src/hooks/__tests__/useRouter.test.ts +17 -0
  138. package/src/hooks/__tests__/useSmoothText.react.test.ts +194 -0
  139. package/src/hooks/__tests__/useSmoothText.test.ts +61 -0
  140. package/src/hooks/__tests__/useTheme.test.ts +118 -0
  141. package/src/hooks/use-mobile.ts +16 -0
  142. package/src/hooks/useAgentPicker.ts +101 -0
  143. package/src/hooks/useMediaQuery.ts +49 -0
  144. package/src/hooks/useMention.ts +236 -0
  145. package/src/hooks/useProjects.ts +88 -0
  146. package/src/hooks/useResyncOnReconnect.ts +28 -0
  147. package/src/hooks/useRouter.ts +92 -0
  148. package/src/hooks/useSessionChat.ts +427 -0
  149. package/src/hooks/useSessionContext.ts +106 -0
  150. package/src/hooks/useSessionList.ts +327 -0
  151. package/src/hooks/useSidebarData.ts +108 -0
  152. package/src/hooks/useSmoothText.ts +122 -0
  153. package/src/hooks/useTheme.ts +164 -0
  154. package/src/hooks/useWebSocket.ts +196 -0
  155. package/src/index.css +27 -0
  156. package/src/index.html +14 -0
  157. package/src/index.ts +49 -0
  158. package/src/lib/__tests__/attachments.test.ts +58 -0
  159. package/src/lib/__tests__/avatar-presets.test.ts +25 -0
  160. package/src/lib/__tests__/collapsible.test.ts +101 -0
  161. package/src/lib/__tests__/composer.test.ts +118 -0
  162. package/src/lib/__tests__/highlight.test.ts +42 -0
  163. package/src/lib/__tests__/home-chat.test.ts +64 -0
  164. package/src/lib/__tests__/lock-screen.test.ts +64 -0
  165. package/src/lib/__tests__/model-picker.test.ts +205 -0
  166. package/src/lib/__tests__/onboarding.test.ts +41 -0
  167. package/src/lib/__tests__/permission-info.test.ts +61 -0
  168. package/src/lib/__tests__/project-extensions.test.ts +83 -0
  169. package/src/lib/__tests__/project-overview.test.ts +188 -0
  170. package/src/lib/__tests__/references.test.ts +121 -0
  171. package/src/lib/__tests__/routes.test.ts +134 -0
  172. package/src/lib/__tests__/session-selection.test.ts +41 -0
  173. package/src/lib/__tests__/session-summary.test.ts +115 -0
  174. package/src/lib/__tests__/session-title.test.ts +58 -0
  175. package/src/lib/__tests__/sidebar-groups.test.ts +100 -0
  176. package/src/lib/__tests__/skill-installs.test.ts +135 -0
  177. package/src/lib/__tests__/skills-catalog.test.ts +124 -0
  178. package/src/lib/__tests__/tone.test.ts +37 -0
  179. package/src/lib/__tests__/turns-delta.test.ts +55 -0
  180. package/src/lib/agent-types.ts +49 -0
  181. package/src/lib/api.ts +306 -0
  182. package/src/lib/attachments.ts +203 -0
  183. package/src/lib/auth.ts +92 -0
  184. package/src/lib/avatar-presets.ts +31 -0
  185. package/src/lib/collapsible.ts +75 -0
  186. package/src/lib/composer.ts +89 -0
  187. package/src/lib/data-events.ts +17 -0
  188. package/src/lib/headlines.ts +30 -0
  189. package/src/lib/highlight.ts +186 -0
  190. package/src/lib/layout.ts +20 -0
  191. package/src/lib/lock-screen.ts +54 -0
  192. package/src/lib/model-picker.ts +133 -0
  193. package/src/lib/onboarding.ts +38 -0
  194. package/src/lib/passkeys.ts +113 -0
  195. package/src/lib/pending-prompt.ts +53 -0
  196. package/src/lib/permission-info.ts +101 -0
  197. package/src/lib/project-extensions.ts +136 -0
  198. package/src/lib/project-overview.ts +123 -0
  199. package/src/lib/prompts.ts +87 -0
  200. package/src/lib/references.ts +106 -0
  201. package/src/lib/routes.ts +124 -0
  202. package/src/lib/session-handoff.ts +25 -0
  203. package/src/lib/session-selection.ts +40 -0
  204. package/src/lib/session-status.ts +45 -0
  205. package/src/lib/session-summary.ts +68 -0
  206. package/src/lib/session-title.ts +63 -0
  207. package/src/lib/sidebar-groups.ts +78 -0
  208. package/src/lib/skill-installs.ts +362 -0
  209. package/src/lib/skills-catalog.ts +138 -0
  210. package/src/lib/time.ts +22 -0
  211. package/src/lib/tone.ts +80 -0
  212. package/src/lib/turns.ts +320 -0
  213. package/src/lib/utils.ts +6 -0
  214. package/src/logo.svg +31 -0
  215. package/src/pages/projects/ProjectDetailPage.tsx +131 -0
  216. package/src/pages/projects/ProjectsPage.tsx +97 -0
  217. package/src/pages/sessions/SessionPage.tsx +122 -0
  218. package/src/pages/settings/SettingsPage.tsx +366 -0
  219. package/src/pages/settings/__tests__/SettingsPage.test.ts +16 -0
  220. package/src/pages/skills/SkillsPage.tsx +745 -0
  221. package/src/paths.ts +121 -0
  222. package/src/runtime.ts +24 -0
  223. package/src/server/__tests__/app.test.ts +1044 -0
  224. package/src/server/__tests__/auth.test.ts +370 -0
  225. package/src/server/__tests__/config.test.ts +101 -0
  226. package/src/server/__tests__/db.test.ts +485 -0
  227. package/src/server/__tests__/host.test.ts +14 -0
  228. package/src/server/__tests__/tunnel-config.test.ts +38 -0
  229. package/src/server/app.ts +427 -0
  230. package/src/server/db/auth.ts +311 -0
  231. package/src/server/db/messages.ts +50 -0
  232. package/src/server/db/projects.ts +106 -0
  233. package/src/server/db/prompts.ts +78 -0
  234. package/src/server/db/rows.ts +150 -0
  235. package/src/server/db/schema.ts +221 -0
  236. package/src/server/db/sessions.ts +239 -0
  237. package/src/server/db/tunnel.ts +124 -0
  238. package/src/server/host.ts +12 -0
  239. package/src/server/result.ts +15 -0
  240. package/src/server/routes/auth-guard.ts +156 -0
  241. package/src/server/routes/auth.routes.ts +287 -0
  242. package/src/server/routes/helpers.ts +165 -0
  243. package/src/server/routes/meta.routes.ts +24 -0
  244. package/src/server/routes/passkeys.routes.ts +161 -0
  245. package/src/server/routes/projects.routes.ts +167 -0
  246. package/src/server/routes/sessions.routes.ts +197 -0
  247. package/src/server/routes/skills.routes.ts +136 -0
  248. package/src/server/routes/tunnel.routes.ts +132 -0
  249. package/src/server/routes/types.ts +35 -0
  250. package/src/server/routes/uploads.routes.ts +54 -0
  251. package/src/server/routes/workspace.routes.ts +127 -0
  252. package/src/server/services/__tests__/app-info.test.ts +18 -0
  253. package/src/server/services/__tests__/attachments.test.ts +159 -0
  254. package/src/server/services/__tests__/auth-passkeys.test.ts +358 -0
  255. package/src/server/services/__tests__/auth.test.ts +341 -0
  256. package/src/server/services/__tests__/claude-code.test.ts +609 -0
  257. package/src/server/services/__tests__/claude-mcp.test.ts +90 -0
  258. package/src/server/services/__tests__/claude-skills.test.ts +63 -0
  259. package/src/server/services/__tests__/frpc-installer.test.ts +83 -0
  260. package/src/server/services/__tests__/git-info.test.ts +74 -0
  261. package/src/server/services/__tests__/opencode.test.ts +320 -0
  262. package/src/server/services/__tests__/project-manager.test.ts +190 -0
  263. package/src/server/services/__tests__/sandbox.test.ts +141 -0
  264. package/src/server/services/__tests__/session-manager.test.ts +3509 -0
  265. package/src/server/services/__tests__/skill-install-jobs.test.ts +245 -0
  266. package/src/server/services/__tests__/skills-registry.test.ts +365 -0
  267. package/src/server/services/__tests__/tunnel-lhr.test.ts +234 -0
  268. package/src/server/services/__tests__/tunnel.test.ts +367 -0
  269. package/src/server/services/__tests__/websocket-gateway.test.ts +677 -0
  270. package/src/server/services/__tests__/workspace.test.ts +249 -0
  271. package/src/server/services/app-info.ts +29 -0
  272. package/src/server/services/attachments.ts +193 -0
  273. package/src/server/services/auth-passkeys.ts +459 -0
  274. package/src/server/services/auth.ts +633 -0
  275. package/src/server/services/claude-code-client.ts +1093 -0
  276. package/src/server/services/claude-code-translate.ts +370 -0
  277. package/src/server/services/claude-mcp.ts +122 -0
  278. package/src/server/services/claude-skills.ts +102 -0
  279. package/src/server/services/frpc-installer.ts +183 -0
  280. package/src/server/services/git-info.ts +87 -0
  281. package/src/server/services/opencode-client.ts +714 -0
  282. package/src/server/services/opencode-server.ts +319 -0
  283. package/src/server/services/opencode-sse.ts +82 -0
  284. package/src/server/services/project-manager.ts +147 -0
  285. package/src/server/services/sandbox.ts +83 -0
  286. package/src/server/services/session-events.ts +256 -0
  287. package/src/server/services/session-manager.ts +1439 -0
  288. package/src/server/services/skill-install-jobs.ts +275 -0
  289. package/src/server/services/skills-registry.ts +480 -0
  290. package/src/server/services/tunnel-api.ts +207 -0
  291. package/src/server/services/tunnel-config.ts +45 -0
  292. package/src/server/services/tunnel-lhr.ts +387 -0
  293. package/src/server/services/tunnel.ts +633 -0
  294. package/src/server/services/turn-stream.ts +339 -0
  295. package/src/server/services/websocket-gateway.ts +374 -0
  296. package/src/server/services/workspace.ts +223 -0
  297. package/src/server/services/ws-transport.ts +121 -0
  298. package/src/types/context.ts +69 -0
  299. package/src/types/index.ts +13 -0
  300. package/src/types/message.ts +58 -0
  301. package/src/types/project.ts +16 -0
  302. package/src/types/prompt.ts +118 -0
  303. package/src/types/session.ts +52 -0
  304. package/src/types/turns.ts +47 -0
  305. package/src/ws-protocol.ts +93 -0
  306. package/styles/globals.css +311 -0
  307. package/tsconfig.json +37 -0
@@ -0,0 +1,85 @@
1
+ import type * as React from "react";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ function MessageGroup({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="message-group"
9
+ className={cn("flex min-w-0 flex-col gap-2", className)}
10
+ {...props}
11
+ />
12
+ );
13
+ }
14
+
15
+ function Message({
16
+ className,
17
+ align = "start",
18
+ ...props
19
+ }: React.ComponentProps<"div"> & { align?: "start" | "end" }) {
20
+ return (
21
+ <div
22
+ data-slot="message"
23
+ data-align={align}
24
+ className={cn(
25
+ "group/message relative flex w-full min-w-0 gap-2 text-base data-[align=end]:flex-row-reverse",
26
+ className,
27
+ )}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
33
+ function MessageAvatar({ className, ...props }: React.ComponentProps<"div">) {
34
+ return (
35
+ <div
36
+ data-slot="message-avatar"
37
+ className={cn(
38
+ "flex w-fit min-w-8 shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-muted group-has-data-[slot=message-footer]/message:-translate-y-8",
39
+ className,
40
+ )}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+
46
+ function MessageContent({ className, ...props }: React.ComponentProps<"div">) {
47
+ return (
48
+ <div
49
+ data-slot="message-content"
50
+ className={cn(
51
+ "flex w-full min-w-0 flex-col gap-2.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end",
52
+ className,
53
+ )}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ function MessageHeader({ className, ...props }: React.ComponentProps<"div">) {
60
+ return (
61
+ <div
62
+ data-slot="message-header"
63
+ className={cn(
64
+ "flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0",
65
+ className,
66
+ )}
67
+ {...props}
68
+ />
69
+ );
70
+ }
71
+
72
+ function MessageFooter({ className, ...props }: React.ComponentProps<"div">) {
73
+ return (
74
+ <div
75
+ data-slot="message-footer"
76
+ className={cn(
77
+ "flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",
78
+ className,
79
+ )}
80
+ {...props}
81
+ />
82
+ );
83
+ }
84
+
85
+ export { Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Popover — pembungkus tipis `radix-ui` Popover (pola komponen ui lain:
3
+ * dropdown-menu, dialog). Konten melayang dengan panah opsional.
4
+ */
5
+ import { Popover as PopoverPrimitive } from "radix-ui";
6
+ import type * as React from "react";
7
+ import { cn } from "@/lib/utils";
8
+
9
+ function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {
10
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />;
11
+ }
12
+
13
+ function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
14
+ return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
15
+ }
16
+
17
+ function PopoverContent({
18
+ className,
19
+ align = "center",
20
+ sideOffset = 4,
21
+ ...props
22
+ }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
23
+ return (
24
+ <PopoverPrimitive.Portal>
25
+ <PopoverPrimitive.Content
26
+ data-slot="popover-content"
27
+ align={align}
28
+ sideOffset={sideOffset}
29
+ className={cn(
30
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out",
31
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95",
32
+ "data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
33
+ "z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-1 shadow-md outline-hidden",
34
+ className,
35
+ )}
36
+ {...props}
37
+ />
38
+ </PopoverPrimitive.Portal>
39
+ );
40
+ }
41
+
42
+ function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
43
+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
44
+ }
45
+
46
+ export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Kolom cari ala GtkSearchEntry (libadwaita): latar terisi tanpa border,
3
+ * ikon kaca pembesar di kiri, tombol hapus bundar saat berisi, ring fokus
4
+ * aksen. Esc mengosongkan isi (bila ada) sebelum diteruskan ke pemanggil.
5
+ *
6
+ * Teks 16px di HP agar iOS Safari tidak men-zoom halaman saat fokus.
7
+ */
8
+ import { SearchIcon, XIcon } from "lucide-react";
9
+ import { type ComponentProps, useRef } from "react";
10
+ import { cn } from "@/lib/utils";
11
+
12
+ export interface SearchEntryProps
13
+ extends Omit<ComponentProps<"input">, "value" | "onChange" | "type"> {
14
+ value: string;
15
+ onValueChange: (value: string) => void;
16
+ /** Kelas untuk pembungkus (lebar, margin). */
17
+ className?: string;
18
+ }
19
+
20
+ export function SearchEntry({
21
+ value,
22
+ onValueChange,
23
+ className,
24
+ onKeyDown,
25
+ ref,
26
+ ...props
27
+ }: SearchEntryProps) {
28
+ const localRef = useRef<HTMLInputElement | null>(null);
29
+ return (
30
+ <div className={cn("relative", className)}>
31
+ <SearchIcon
32
+ className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground"
33
+ aria-hidden
34
+ />
35
+ <input
36
+ ref={(el) => {
37
+ localRef.current = el;
38
+ if (typeof ref === "function") ref(el);
39
+ else if (ref) ref.current = el;
40
+ }}
41
+ type="search"
42
+ value={value}
43
+ onChange={(e) => onValueChange(e.target.value)}
44
+ onKeyDown={(e) => {
45
+ onKeyDown?.(e);
46
+ if (e.defaultPrevented) return;
47
+ if (e.key === "Escape" && value !== "") {
48
+ e.preventDefault();
49
+ onValueChange("");
50
+ }
51
+ }}
52
+ className="h-9 w-full rounded-lg bg-black/[0.06] pr-9 pl-9 text-base outline-none transition-shadow placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring/60 sm:text-sm dark:bg-white/[0.08] [&::-webkit-search-cancel-button]:hidden"
53
+ {...props}
54
+ />
55
+ {value !== "" && (
56
+ <button
57
+ type="button"
58
+ onClick={() => {
59
+ onValueChange("");
60
+ localRef.current?.focus();
61
+ }}
62
+ aria-label="Clear search"
63
+ className="absolute top-1/2 right-1.5 flex size-6 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-black/10 hover:text-foreground dark:hover:bg-white/10"
64
+ >
65
+ <XIcon className="size-3.5" />
66
+ </button>
67
+ )}
68
+ </div>
69
+ );
70
+ }
@@ -0,0 +1,172 @@
1
+ "use client";
2
+
3
+ import * as SelectPrimitive from "@radix-ui/react-select";
4
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
5
+ import type * as React from "react";
6
+
7
+ import { cn } from "@/lib/utils";
8
+
9
+ function Select({ ...props }: React.ComponentProps<typeof SelectPrimitive.Root>) {
10
+ return <SelectPrimitive.Root data-slot="select" {...props} />;
11
+ }
12
+
13
+ function SelectGroup({ ...props }: React.ComponentProps<typeof SelectPrimitive.Group>) {
14
+ return <SelectPrimitive.Group data-slot="select-group" {...props} />;
15
+ }
16
+
17
+ function SelectValue({ ...props }: React.ComponentProps<typeof SelectPrimitive.Value>) {
18
+ return <SelectPrimitive.Value data-slot="select-value" {...props} />;
19
+ }
20
+
21
+ function SelectTrigger({
22
+ className,
23
+ size = "default",
24
+ children,
25
+ ...props
26
+ }: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
27
+ size?: "sm" | "default";
28
+ }) {
29
+ return (
30
+ <SelectPrimitive.Trigger
31
+ data-slot="select-trigger"
32
+ data-size={size}
33
+ className={cn(
34
+ "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
35
+ className,
36
+ )}
37
+ {...props}
38
+ >
39
+ {children}
40
+ <SelectPrimitive.Icon asChild>
41
+ <ChevronDownIcon className="size-4 opacity-50" />
42
+ </SelectPrimitive.Icon>
43
+ </SelectPrimitive.Trigger>
44
+ );
45
+ }
46
+
47
+ function SelectContent({
48
+ className,
49
+ children,
50
+ position = "popper",
51
+ align = "center",
52
+ ...props
53
+ }: React.ComponentProps<typeof SelectPrimitive.Content>) {
54
+ return (
55
+ <SelectPrimitive.Portal>
56
+ <SelectPrimitive.Content
57
+ data-slot="select-content"
58
+ className={cn(
59
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
60
+ position === "popper" &&
61
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
62
+ className,
63
+ )}
64
+ position={position}
65
+ align={align}
66
+ {...props}
67
+ >
68
+ <SelectScrollUpButton />
69
+ <SelectPrimitive.Viewport
70
+ className={cn(
71
+ "p-1",
72
+ position === "popper" &&
73
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1",
74
+ )}
75
+ >
76
+ {children}
77
+ </SelectPrimitive.Viewport>
78
+ <SelectScrollDownButton />
79
+ </SelectPrimitive.Content>
80
+ </SelectPrimitive.Portal>
81
+ );
82
+ }
83
+
84
+ function SelectLabel({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.Label>) {
85
+ return (
86
+ <SelectPrimitive.Label
87
+ data-slot="select-label"
88
+ className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
89
+ {...props}
90
+ />
91
+ );
92
+ }
93
+
94
+ function SelectItem({
95
+ className,
96
+ children,
97
+ ...props
98
+ }: React.ComponentProps<typeof SelectPrimitive.Item>) {
99
+ return (
100
+ <SelectPrimitive.Item
101
+ data-slot="select-item"
102
+ className={cn(
103
+ "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
104
+ className,
105
+ )}
106
+ {...props}
107
+ >
108
+ <span className="absolute right-2 flex size-3.5 items-center justify-center">
109
+ <SelectPrimitive.ItemIndicator>
110
+ <CheckIcon className="size-4" />
111
+ </SelectPrimitive.ItemIndicator>
112
+ </span>
113
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
114
+ </SelectPrimitive.Item>
115
+ );
116
+ }
117
+
118
+ function SelectSeparator({
119
+ className,
120
+ ...props
121
+ }: React.ComponentProps<typeof SelectPrimitive.Separator>) {
122
+ return (
123
+ <SelectPrimitive.Separator
124
+ data-slot="select-separator"
125
+ className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
126
+ {...props}
127
+ />
128
+ );
129
+ }
130
+
131
+ function SelectScrollUpButton({
132
+ className,
133
+ ...props
134
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
135
+ return (
136
+ <SelectPrimitive.ScrollUpButton
137
+ data-slot="select-scroll-up-button"
138
+ className={cn("flex cursor-default items-center justify-center py-1", className)}
139
+ {...props}
140
+ >
141
+ <ChevronUpIcon className="size-4" />
142
+ </SelectPrimitive.ScrollUpButton>
143
+ );
144
+ }
145
+
146
+ function SelectScrollDownButton({
147
+ className,
148
+ ...props
149
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
150
+ return (
151
+ <SelectPrimitive.ScrollDownButton
152
+ data-slot="select-scroll-down-button"
153
+ className={cn("flex cursor-default items-center justify-center py-1", className)}
154
+ {...props}
155
+ >
156
+ <ChevronDownIcon className="size-4" />
157
+ </SelectPrimitive.ScrollDownButton>
158
+ );
159
+ }
160
+
161
+ export {
162
+ Select,
163
+ SelectContent,
164
+ SelectGroup,
165
+ SelectItem,
166
+ SelectLabel,
167
+ SelectScrollDownButton,
168
+ SelectScrollUpButton,
169
+ SelectSeparator,
170
+ SelectTrigger,
171
+ SelectValue,
172
+ };
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import { Separator as SeparatorPrimitive } from "radix-ui";
4
+ import type * as React from "react";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function Separator({
9
+ className,
10
+ orientation = "horizontal",
11
+ decorative = true,
12
+ ...props
13
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
14
+ return (
15
+ <SeparatorPrimitive.Root
16
+ data-slot="separator"
17
+ decorative={decorative}
18
+ orientation={orientation}
19
+ className={cn(
20
+ "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
21
+ className,
22
+ )}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ export { Separator };
@@ -0,0 +1,138 @@
1
+ import { XIcon } from "lucide-react";
2
+ import { Dialog as SheetPrimitive } from "radix-ui";
3
+ import type * as React from "react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
8
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />;
9
+ }
10
+
11
+ function SheetTrigger({ ...props }: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
12
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
13
+ }
14
+
15
+ function SheetClose({ ...props }: React.ComponentProps<typeof SheetPrimitive.Close>) {
16
+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
17
+ }
18
+
19
+ function SheetPortal({ ...props }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
20
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
21
+ }
22
+
23
+ function SheetOverlay({
24
+ className,
25
+ ...props
26
+ }: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
27
+ return (
28
+ <SheetPrimitive.Overlay
29
+ data-slot="sheet-overlay"
30
+ className={cn(
31
+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function SheetContent({
40
+ className,
41
+ children,
42
+ side = "right",
43
+ showCloseButton = true,
44
+ ...props
45
+ }: React.ComponentProps<typeof SheetPrimitive.Content> & {
46
+ side?: "top" | "right" | "bottom" | "left";
47
+ showCloseButton?: boolean;
48
+ }) {
49
+ return (
50
+ <SheetPortal>
51
+ <SheetOverlay />
52
+ <SheetPrimitive.Content
53
+ data-slot="sheet-content"
54
+ className={cn(
55
+ "fixed z-50 flex flex-col gap-4 bg-background shadow-lg transition ease-in-out data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:animate-in data-[state=open]:duration-500",
56
+ side === "right" &&
57
+ "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
58
+ side === "left" &&
59
+ "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
60
+ side === "top" &&
61
+ "inset-x-0 top-0 h-auto border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
62
+ side === "bottom" &&
63
+ "inset-x-0 bottom-0 h-auto max-h-[85vh] rounded-t-2xl data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
64
+ className,
65
+ )}
66
+ {...props}
67
+ >
68
+ {side === "bottom" && (
69
+ <div
70
+ className="mx-auto mt-2 h-1.5 w-10 shrink-0 rounded-full bg-muted-foreground/30"
71
+ aria-hidden
72
+ />
73
+ )}
74
+ {children}
75
+ {showCloseButton && side !== "bottom" && (
76
+ <SheetPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
77
+ <XIcon className="size-4" />
78
+ <span className="sr-only">Close</span>
79
+ </SheetPrimitive.Close>
80
+ )}
81
+ </SheetPrimitive.Content>
82
+ </SheetPortal>
83
+ );
84
+ }
85
+
86
+ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
87
+ return (
88
+ <div
89
+ data-slot="sheet-header"
90
+ className={cn("flex flex-col gap-1.5 p-4", className)}
91
+ {...props}
92
+ />
93
+ );
94
+ }
95
+
96
+ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
97
+ return (
98
+ <div
99
+ data-slot="sheet-footer"
100
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
101
+ {...props}
102
+ />
103
+ );
104
+ }
105
+
106
+ function SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {
107
+ return (
108
+ <SheetPrimitive.Title
109
+ data-slot="sheet-title"
110
+ className={cn("font-semibold text-foreground", className)}
111
+ {...props}
112
+ />
113
+ );
114
+ }
115
+
116
+ function SheetDescription({
117
+ className,
118
+ ...props
119
+ }: React.ComponentProps<typeof SheetPrimitive.Description>) {
120
+ return (
121
+ <SheetPrimitive.Description
122
+ data-slot="sheet-description"
123
+ className={cn("text-sm text-muted-foreground", className)}
124
+ {...props}
125
+ />
126
+ );
127
+ }
128
+
129
+ export {
130
+ Sheet,
131
+ SheetClose,
132
+ SheetContent,
133
+ SheetDescription,
134
+ SheetFooter,
135
+ SheetHeader,
136
+ SheetTitle,
137
+ SheetTrigger,
138
+ };