Ethscriptions commited on
Commit
363c51a
·
verified ·
1 Parent(s): dfbd839

Step 1: 项目计划与分镜大纲 README

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +79 -0
  2. index.html +1 -0
  3. scripts/placeholder.html +1 -0
  4. scripts/push_hf.py +36 -0
  5. src/App.tsx +24 -0
  6. src/ErrorBoundary.tsx +106 -0
  7. src/components/ui/accordion.tsx +55 -0
  8. src/components/ui/alert.tsx +59 -0
  9. src/components/ui/aspect-ratio.tsx +5 -0
  10. src/components/ui/avatar.tsx +48 -0
  11. src/components/ui/badge.tsx +36 -0
  12. src/components/ui/breadcrumb.tsx +115 -0
  13. src/components/ui/button.tsx +57 -0
  14. src/components/ui/calendar.tsx +211 -0
  15. src/components/ui/card.tsx +76 -0
  16. src/components/ui/carousel.tsx +262 -0
  17. src/components/ui/checkbox.tsx +30 -0
  18. src/components/ui/collapsible.tsx +9 -0
  19. src/components/ui/command.tsx +153 -0
  20. src/components/ui/context-menu.tsx +200 -0
  21. src/components/ui/dialog.tsx +120 -0
  22. src/components/ui/drawer.tsx +118 -0
  23. src/components/ui/dropdown-menu.tsx +201 -0
  24. src/components/ui/form.tsx +176 -0
  25. src/components/ui/hover-card.tsx +29 -0
  26. src/components/ui/input.tsx +22 -0
  27. src/components/ui/label.tsx +26 -0
  28. src/components/ui/menubar.tsx +256 -0
  29. src/components/ui/navigation-menu.tsx +128 -0
  30. src/components/ui/popover.tsx +33 -0
  31. src/components/ui/progress.tsx +26 -0
  32. src/components/ui/radio-group.tsx +42 -0
  33. src/components/ui/resizable.tsx +43 -0
  34. src/components/ui/scroll-area.tsx +46 -0
  35. src/components/ui/select.tsx +157 -0
  36. src/components/ui/separator.tsx +31 -0
  37. src/components/ui/sheet.tsx +140 -0
  38. src/components/ui/skeleton.tsx +15 -0
  39. src/components/ui/slider.tsx +26 -0
  40. src/components/ui/sonner.tsx +29 -0
  41. src/components/ui/switch.tsx +29 -0
  42. src/components/ui/table.tsx +120 -0
  43. src/components/ui/tabs.tsx +53 -0
  44. src/components/ui/textarea.tsx +22 -0
  45. src/components/ui/toast.tsx +129 -0
  46. src/components/ui/toaster.tsx +35 -0
  47. src/components/ui/toggle-group.tsx +59 -0
  48. src/components/ui/toggle.tsx +43 -0
  49. src/components/ui/tooltip.tsx +30 -0
  50. src/entry-client.tsx +112 -0
README.md ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: 腱鞘炎 3D 医学科普动画
3
+ emoji: 🖐️
4
+ colorFrom: indigo
5
+ colorTo: red
6
+ sdk: static
7
+ app_file: index.html
8
+ pinned: false
9
+ ---
10
+
11
+ # 腱鞘炎 · 3D 人体解剖科普动画(网页实时渲染版)
12
+
13
+ > 类型:医学科普 + 3D 人体解剖动画 | 时长:约 80–90 秒 | 画幅:9:16 竖屏 | 风格:真实医学 3D、清晰、干净、专业、易懂
14
+ > 所有解剖结构、人物手部、器械均为**原创程序化 3D 医学示意模型**,不使用任何医院、品牌、教材或受版权保护的现成素材。
15
+
16
+ ---
17
+
18
+ ## 一、项目目标
19
+
20
+ 1. 在网页中直接播放一部完整的 3D 动画视频(不是预渲染 mp4),含 **旁白配音 + 背景音乐 + 音效 + 字幕**。
21
+ 2. 网页内 **浏览器本地渲染导出 MP4**(WebCodecs 编码,H.264 + AAC/Opus),可选 **24 / 30 / 60 fps**,输出 1080×1920。
22
+ 3. **适配手机竖屏观看**:播放器自动铺满竖屏,支持全屏、点击暂停、拖动进度。
23
+ 4. 提供四大平台(小红书 / 抖音 / 哔哩哔哩 / 微信视频号)可一键复制的 **标题、描述、话题标签**,并按各平台规格生成 **封面图**(PNG 下载)。
24
+
25
+ ## 二、技术方案
26
+
27
+ | 模块 | 技术 |
28
+ |---|---|
29
+ | 3D 渲染 | Three.js(WebGL),程序化建模:手部皮肤、骨骼、肌腱、腱鞘、器械、生活道具 |
30
+ | 分层透视 | 皮肤「深度预写 + 半透明」双通道渲染,实现透明皮肤无接缝;骨骼 / 肌腱 / 腱鞘三层独立淡入淡出 |
31
+ | 疼痛表达 | 统一红色发光(加色混合 Sprite + 肌腱着色器中的摩擦热区 黄→红) |
32
+ | 肌腱滑动 | 每帧按关节世界坐标重建 Tube 曲线,纹理 UV 随拇指屈伸偏移,表现肌腱在腱鞘内滑动 |
33
+ | 时间轴 | 纯函数 `timeline(t) → 场景状态`,同一时刻永远得到同一画面(保证导出逐帧一致) |
34
+ | 2D 叠加层 | Canvas 2D 绘制字幕、关键词、标注、超声画面、四宫格、分屏标签 |
35
+ | 旁白 | 中文神经网络 TTS 预生成逐句音频(构建期生成,存于 `public/audio`),句级精确字幕对齐 |
36
+ | 音乐/音效 | Web Audio `OfflineAudioContext` 程序化合成:氛围铺底和弦、低频脉冲、转场 whoosh、疼痛刺痛音、扫描提示音、松解释放音;旁白时自动压低音乐(ducking) |
37
+ | 导出 | mediabunny(MP4 封装)+ WebCodecs `VideoEncoder/AudioEncoder`,逐帧离线渲染,不依赖实时速度 |
38
+ | 封面 | 同一 3D 场景按 4:3 / 3:4 / 16:9 重新取景渲染 + 排版标题 |
39
+
40
+ ## 三、分镜大纲(11 个镜头 + 结尾互动)
41
+
42
+ | # | 镜头 | 关键画面 |
43
+ |---|---|---|
44
+ | 1 | 疼痛开始 | 手部特写,拇指活动、抓握 → 疼痛抖动 → 推镜至腕部拇指侧 → 红色发光 → 皮肤半透明 → 骨骼 / 肌腱 / 腱鞘逐层出现 |
45
+ | 2 | 腱鞘炎发生了什么 | 肌腱在腱鞘中顺畅滑动 → 腱鞘增厚、空间变窄 → 摩擦区 黄→红;横截面插图;关键词「腱鞘增厚 / 空间变窄 / 活动疼痛」 |
46
+ | 3 | 为什么一动就疼 | 抓、捏、拧瓶盖三个动作,内部肌腱同步运动,红区随动作增强,镜头跟随疼痛点 |
47
+ | 4 | 哪些人更容易出现 | 键盘鼠标 / 工具抓握 / 家务拧提 / 抱孩子 四场景快切,红点闪现 + 关键词 |
48
+ | 5 | 不要一上来就治疗 | 3D 手旋转 → 锁定疼痛区 → 肌腱轨迹 → 超声画面(肌腱 / 腱鞘 / 狭窄位置) |
49
+ | 6 | 常见治疗方法 | 四宫格:减少抓握 / 拇指腕部支具 / 局部治疗示意 / 手术松解示意 |
50
+ | 7 | 进入小针刀 | 腱鞘放大,红区标记狭窄,原创器械从皮肤外缓慢进入 |
51
+ | 8 | 治疗过程 | 超声定位白点 → 消毒 → 局部麻醉 → 器械到达目标区域松解(3D 剖面,无血腥) |
52
+ | 9 | 松解前后对比 | 左右分屏:治疗前(狭窄、受限、红)vs 治疗后示意(变宽、顺滑、红区减弱) |
53
+ | 10 | 从疼痛到恢复 | 拇指困难 → 轻微活动 → 抓握 → 拿杯子 → 用手机 → 自然活动,红区渐淡 |
54
+ | 11 | 记忆点 | 再次进入 3D → 拉远恢复完整手部 → 红区消失 → 金句「手越疼,越不能只靠硬扛。」 |
55
+ | 尾 | 评论区互动 | 「评论区告诉我,是抓、捏、拧,还是抱孩子?」 |
56
+
57
+ ## 四、实施步骤(每完成一大步推送一次到本 Space)
58
+
59
+ 1. ✅ 编写本计划 README,初始化 Space(static)。
60
+ 2. ⏳ 生成中文旁白逐句音频 + 时间轴清单(`timeline.json`)。
61
+ 3. ⏳ 程序化 3D 手部解剖模型:皮肤 / 骨骼 / 肌腱 / 腱鞘 / 关节骨架绑定 / 疼痛发光 / 器械与道具。
62
+ 4. ⏳ 分镜时间轴:11 镜头 + 结尾的相机运动、姿态、图层、字幕、HUD。
63
+ 5. ⏳ 程序化音乐与音效、旁白混音、ducking。
64
+ 6. ⏳ 播放器 UI(手机竖屏适配、全屏、进度条)+ MP4 本地导出(24/30/60fps)。
65
+ 7. ⏳ 四平台文案(标题 / 话题 / 正文,严格字数校验)+ 封面生成与下载。
66
+ 8. ⏳ 生产构建、检查,推送最终静态站点。
67
+
68
+ ## 五、目录结构(推送到 Space 的内容)
69
+
70
+ ```
71
+ index.html, assets/ ← 构建产物,Space 直接可运行
72
+ audio/ ← 旁白音频 + timeline.json
73
+ src/ ← 源码(engine / scenes / audio / export / covers / copy)
74
+ README.md ← 本文件(计划 + 进度)
75
+ ```
76
+
77
+ ## 六、进度日志
78
+
79
+ - [x] Step 1:计划与大纲完成
index.html ADDED
@@ -0,0 +1 @@
 
 
1
+ <!doctype html><html lang="zh"><meta charset="utf-8"><title>腱鞘炎 3D 科普动画 · 制作中</title><body style="background:#06101f;color:#cfe;font-family:sans-serif;display:grid;place-items:center;height:100vh"><div><h2>腱鞘炎 3D 医学科普动画</h2><p>项目制作中,进度见 README。</p></div></body></html>
scripts/placeholder.html ADDED
@@ -0,0 +1 @@
 
 
1
+ <!doctype html><html lang="zh"><meta charset="utf-8"><title>腱鞘炎 3D 科普动画 · 制作中</title><body style="background:#06101f;color:#cfe;font-family:sans-serif;display:grid;place-items:center;height:100vh"><div><h2>腱鞘炎 3D 医学科普动画</h2><p>项目制作中,进度见 README。</p></div></body></html>
scripts/push_hf.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Push project progress to HF Space Ethscriptions/mx.
2
+ Usage: HF_TOKEN=... python3 scripts/push_hf.py "commit message"
3
+ Uploads README.md, frontend/src -> src/, frontend/public/audio -> audio/, and built static site (dist_static) at root.
4
+ """
5
+ import os, sys
6
+ from huggingface_hub import HfApi, CommitOperationAdd
7
+
8
+ REPO = "Ethscriptions/mx"
9
+ api = HfApi(token=os.environ["HF_TOKEN"])
10
+ root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
11
+ msg = sys.argv[1] if len(sys.argv) > 1 else "update"
12
+ ops = []
13
+
14
+ def add_dir(local, remote_prefix):
15
+ if not os.path.isdir(local):
16
+ return
17
+ for dp, _, files in os.walk(local):
18
+ for f in files:
19
+ lp = os.path.join(dp, f)
20
+ rel = os.path.relpath(lp, local).replace(os.sep, "/")
21
+ ops.append(CommitOperationAdd(path_in_repo=f"{remote_prefix}{rel}", path_or_fileobj=lp))
22
+
23
+ ops.append(CommitOperationAdd(path_in_repo="README.md", path_or_fileobj=os.path.join(root, "README.md")))
24
+ add_dir(os.path.join(root, "frontend", "src"), "src/")
25
+ add_dir(os.path.join(root, "scripts"), "scripts/")
26
+ static = os.path.join(root, "dist_static")
27
+ if os.path.isdir(static):
28
+ add_dir(static, "") # includes index.html, assets/, audio/
29
+ else:
30
+ add_dir(os.path.join(root, "frontend", "public", "audio"), "audio/")
31
+ placeholder = os.path.join(root, "scripts", "placeholder.html")
32
+ if os.path.exists(placeholder):
33
+ ops.append(CommitOperationAdd(path_in_repo="index.html", path_or_fileobj=placeholder))
34
+
35
+ api.create_commit(repo_id=REPO, repo_type="space", operations=ops, commit_message=msg)
36
+ print("pushed", len(ops), "files:", msg)
src/App.tsx ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const SURF_LOGO_PATH = "M14.6875 13.333C15.0977 13.333 15.4859 13.5196 15.7422 13.8398L17.8721 16.502C17.9009 16.538 17.9308 16.5721 17.9619 16.6035C18.3091 16.9542 18.7529 17.3405 18.7529 17.834C18.7528 18.3399 18.3428 18.75 17.8369 18.75H17.5713C17.2123 18.75 16.8727 18.5869 16.6484 18.3066L15.4678 16.8311C14.761 15.9479 13.3369 16.4478 13.3369 17.5791V18.6494C13.3369 18.7062 13.2882 18.75 13.2314 18.75H11.3584C10.7127 18.75 10.1016 18.4564 9.69824 17.9521L8.80176 16.8311C8.09497 15.9477 6.66993 16.4478 6.66992 17.5791V18.6494C6.66991 18.7062 6.62124 18.75 6.56445 18.75H4.69141C4.04589 18.75 3.43548 18.4562 3.03223 17.9521L2.13477 16.8311C1.77885 16.3862 1.25293 15.931 1.25293 15.3613V13.4395C1.253 13.3822 1.29729 13.3331 1.35449 13.333C1.7646 13.3332 2.15295 13.5196 2.40918 13.8398L4.53809 16.502C5.24478 17.3853 6.66962 16.886 6.66992 15.7549V14.6836C6.67018 13.9379 7.27483 13.3332 8.02051 13.333C8.43065 13.333 8.81884 13.5197 9.0752 13.8398L11.2051 16.502C11.9097 17.3827 13.3282 16.8887 13.3369 15.7646V14.6836C13.3372 13.9378 13.9417 13.3331 14.6875 13.333ZM14.6875 6.66699C15.0978 6.66702 15.4859 6.85347 15.7422 7.17383L17.8721 9.83594C18.2276 10.2801 18.7526 10.7339 18.7529 11.3027V13.2266C18.7528 13.2833 18.7091 13.333 18.6523 13.333C18.2421 13.3329 17.8539 13.1465 17.5977 12.8262L15.4678 10.1641C14.7609 9.28113 13.3369 9.78089 13.3369 10.9121V11.9824C13.3369 12.7282 12.7321 13.3328 11.9863 13.333C11.5761 13.3329 11.187 13.1465 10.9307 12.8262L8.80176 10.1641C8.09497 9.28072 6.66993 9.78076 6.66992 10.9121V11.9824C6.66992 12.7284 6.06525 13.3329 5.31934 13.333C4.90908 13.3329 4.52094 13.1465 4.26465 12.8262L2.13477 10.1641C1.7788 9.71925 1.25293 9.26403 1.25293 8.69434V6.77344C1.253 6.71621 1.29728 6.6671 1.35449 6.66699C1.76461 6.66714 2.15295 6.85359 2.40918 7.17383L4.53809 9.83594C5.24487 10.7193 6.66989 10.2192 6.66992 9.08789V8.01758C6.66992 7.27168 7.27467 6.66722 8.02051 6.66699C8.43082 6.66699 8.81885 6.85345 9.0752 7.17383L11.2051 9.83594C11.9097 10.7163 13.3279 10.2223 13.3369 9.09863V8.01758C13.3369 7.27161 13.9416 6.6671 14.6875 6.66699ZM2.43555 1.25C2.79488 1.25 3.13499 1.41367 3.35938 1.69434L4.53809 3.16895C5.24485 4.05241 6.66991 3.55228 6.66992 2.4209V1.35059C6.66993 1.29391 6.71874 1.25017 6.77539 1.25H8.64844C9.29394 1.25 9.90438 1.5438 10.3076 2.04785L11.2051 3.16895C11.9097 4.04967 13.3282 3.55564 13.3369 2.43164V1.35059C13.3369 1.29391 13.3857 1.25017 13.4424 1.25H15.3154C15.9609 1.25 16.5714 1.5438 16.9746 2.04785L17.8721 3.16895C18.2275 3.61314 18.7526 4.06689 18.7529 4.63574V6.56055C18.7528 6.61728 18.7091 6.66699 18.6523 6.66699C18.2421 6.66688 17.8539 6.4795 17.5977 6.15918L15.4678 3.49805C14.761 2.61474 13.3371 3.11388 13.3369 4.24512V5.31543C13.3369 6.06126 12.7321 6.66673 11.9863 6.66699C11.5761 6.66693 11.187 6.47955 10.9307 6.15918L8.80176 3.49805C8.095 2.61474 6.67006 3.11388 6.66992 4.24512V5.31543C6.66992 6.06137 6.06525 6.66691 5.31934 6.66699C4.90908 6.66693 4.52094 6.47955 4.26465 6.15918L2.13477 3.49805C2.10558 3.46158 2.07543 3.42725 2.04395 3.39551C1.6966 3.04528 1.25293 2.6583 1.25293 2.16504C1.25317 1.65963 1.66348 1.25 2.16895 1.25H2.43555Z"
2
+
3
+ function SurfLogo() {
4
+ return (
5
+ <svg width="40" height="40" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
6
+ <path d={SURF_LOGO_PATH} fill="var(--fg-base, #212121)" />
7
+ </svg>
8
+ )
9
+ }
10
+
11
+ // Frontend API calls should use `src/lib/api.ts`, not absolute `/api/...` URLs.
12
+ export default function App() {
13
+ return (
14
+ <div data-surf-placeholder className="min-h-screen flex flex-col items-center justify-center gap-4 rounded-[14px]" style={{ background: 'var(--bg-chat-nav, #f4f4f4)', border: '1px solid var(--border-strong, rgba(42,42,42,0.08))' }}>
15
+ <SurfLogo />
16
+ <h1 className="font-black text-[36px] leading-[44px] text-[var(--fg-base,#212121)]">
17
+ GM, Builder<span style={{ color: 'var(--brand-100, #ff2882)' }}>.</span>
18
+ </h1>
19
+ <p className="text-xl leading-7 text-[var(--fg-subtle,#7a7a7a)]">
20
+ Start your crypto project here!
21
+ </p>
22
+ </div>
23
+ )
24
+ }
src/ErrorBoundary.tsx ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, type ReactNode } from 'react'
2
+
3
+ interface Props { children: ReactNode; fallback?: ReactNode }
4
+ interface State { error: Error | null }
5
+
6
+ // Auto-reload config for transient dep-optimization errors.
7
+ // Shares the same key as entry-client's cold-start guard so reload
8
+ // attempts are counted together, preventing double reload loops.
9
+ const RELOAD_KEY = '__dep_reload'
10
+ const MAX_RELOADS = 6
11
+ // If the last reload was more than this many ms ago, reset the counter.
12
+ // This prevents stale counters from blocking legitimate retries on a
13
+ // later visit, while still capping rapid reload loops.
14
+ const RELOAD_WINDOW_MS = 60_000
15
+
16
+ // Patterns that indicate React modules loaded as stubs (dep optimization in progress)
17
+ const DEP_OPT_PATTERNS = [
18
+ "reading 'useState'",
19
+ "reading 'useEffect'",
20
+ "reading 'useRef'",
21
+ "reading 'useCallback'",
22
+ "reading 'useMemo'",
23
+ "reading 'useContext'",
24
+ "reading 'useReducer'",
25
+ ]
26
+
27
+ function isDepOptError(msg: string): boolean {
28
+ return DEP_OPT_PATTERNS.some((p) => msg.includes(p))
29
+ }
30
+
31
+ // Shared format with entry-client: { c: count, t: timestamp }
32
+ function getReloadState(): { c: number; t: number } {
33
+ try {
34
+ const raw = sessionStorage.getItem(RELOAD_KEY)
35
+ if (raw) return JSON.parse(raw)
36
+ } catch { /* ignore parse errors */ }
37
+ return { c: 0, t: 0 }
38
+ }
39
+
40
+ export default class ErrorBoundary extends Component<Props, State> {
41
+ state: State = { error: null }
42
+
43
+ static getDerivedStateFromError(error: Error) {
44
+ return { error }
45
+ }
46
+
47
+ componentDidCatch(error: Error) {
48
+ if (!isDepOptError(error.message)) return
49
+ // Vite dep optimization may serve React stubs during cold start.
50
+ // Auto-reload so the browser fetches the real modules once ready.
51
+ const prev = getReloadState()
52
+ // Reset counter if outside the rapid-reload window (stale from earlier visit)
53
+ const count = (Date.now() - prev.t > RELOAD_WINDOW_MS) ? 0 : prev.c
54
+ if (count < MAX_RELOADS) {
55
+ sessionStorage.setItem(RELOAD_KEY, JSON.stringify({ c: count + 1, t: Date.now() }))
56
+ setTimeout(() => location.reload(), 3000)
57
+ }
58
+ }
59
+
60
+ render() {
61
+ if (this.state.error) {
62
+ if (this.props.fallback) return this.props.fallback
63
+
64
+ // Show a friendlier message for dep optimization errors that will auto-reload
65
+ if (isDepOptError(this.state.error.message)) {
66
+ const { c, t } = getReloadState()
67
+ const fresh = (Date.now() - t <= RELOAD_WINDOW_MS)
68
+ if (!fresh || c < MAX_RELOADS) {
69
+ return (
70
+ <div style={{
71
+ padding: '24px',
72
+ margin: '16px',
73
+ borderRadius: '12px',
74
+ background: 'rgba(59,130,246,0.06)',
75
+ border: '1px solid rgba(59,130,246,0.15)',
76
+ color: '#3b82f6',
77
+ fontSize: '13px',
78
+ fontFamily: 'system-ui, sans-serif',
79
+ textAlign: 'center',
80
+ }}>
81
+ <p style={{ fontWeight: 600, marginBottom: '4px' }}>Loading dependencies...</p>
82
+ <p style={{ opacity: 0.7, fontSize: '12px' }}>Reloading automatically</p>
83
+ </div>
84
+ )
85
+ }
86
+ }
87
+
88
+ return (
89
+ <div style={{
90
+ padding: '24px',
91
+ margin: '16px',
92
+ borderRadius: '12px',
93
+ background: 'rgba(245,34,45,0.06)',
94
+ border: '1px solid rgba(245,34,45,0.15)',
95
+ color: '#c0392b',
96
+ fontSize: '13px',
97
+ fontFamily: 'monospace',
98
+ }}>
99
+ <p style={{ fontWeight: 600, marginBottom: '8px' }}>Component Error</p>
100
+ <p style={{ opacity: 0.8 }}>{this.state.error.message}</p>
101
+ </div>
102
+ )
103
+ }
104
+ return this.props.children
105
+ }
106
+ }
src/components/ui/accordion.tsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as AccordionPrimitive from "@radix-ui/react-accordion"
3
+ import { ChevronDown } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const Accordion = AccordionPrimitive.Root
8
+
9
+ const AccordionItem = React.forwardRef<
10
+ React.ElementRef<typeof AccordionPrimitive.Item>,
11
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
12
+ >(({ className, ...props }, ref) => (
13
+ <AccordionPrimitive.Item
14
+ ref={ref}
15
+ className={cn("border-b", className)}
16
+ {...props}
17
+ />
18
+ ))
19
+ AccordionItem.displayName = "AccordionItem"
20
+
21
+ const AccordionTrigger = React.forwardRef<
22
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
23
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
24
+ >(({ className, children, ...props }, ref) => (
25
+ <AccordionPrimitive.Header className="flex">
26
+ <AccordionPrimitive.Trigger
27
+ ref={ref}
28
+ className={cn(
29
+ "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
30
+ className
31
+ )}
32
+ {...props}
33
+ >
34
+ {children}
35
+ <ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
36
+ </AccordionPrimitive.Trigger>
37
+ </AccordionPrimitive.Header>
38
+ ))
39
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
40
+
41
+ const AccordionContent = React.forwardRef<
42
+ React.ElementRef<typeof AccordionPrimitive.Content>,
43
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
44
+ >(({ className, children, ...props }, ref) => (
45
+ <AccordionPrimitive.Content
46
+ ref={ref}
47
+ className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
48
+ {...props}
49
+ >
50
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
51
+ </AccordionPrimitive.Content>
52
+ ))
53
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName
54
+
55
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
src/components/ui/alert.tsx ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const alertVariants = cva(
7
+ "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-background text-foreground",
12
+ destructive:
13
+ "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ }
20
+ )
21
+
22
+ const Alert = React.forwardRef<
23
+ HTMLDivElement,
24
+ React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
25
+ >(({ className, variant, ...props }, ref) => (
26
+ <div
27
+ ref={ref}
28
+ role="alert"
29
+ className={cn(alertVariants({ variant }), className)}
30
+ {...props}
31
+ />
32
+ ))
33
+ Alert.displayName = "Alert"
34
+
35
+ const AlertTitle = React.forwardRef<
36
+ HTMLParagraphElement,
37
+ React.HTMLAttributes<HTMLHeadingElement>
38
+ >(({ className, ...props }, ref) => (
39
+ <h5
40
+ ref={ref}
41
+ className={cn("mb-1 font-medium leading-none tracking-tight", className)}
42
+ {...props}
43
+ />
44
+ ))
45
+ AlertTitle.displayName = "AlertTitle"
46
+
47
+ const AlertDescription = React.forwardRef<
48
+ HTMLParagraphElement,
49
+ React.HTMLAttributes<HTMLParagraphElement>
50
+ >(({ className, ...props }, ref) => (
51
+ <div
52
+ ref={ref}
53
+ className={cn("text-sm [&_p]:leading-relaxed", className)}
54
+ {...props}
55
+ />
56
+ ))
57
+ AlertDescription.displayName = "AlertDescription"
58
+
59
+ export { Alert, AlertTitle, AlertDescription }
src/components/ui/aspect-ratio.tsx ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
2
+
3
+ const AspectRatio = AspectRatioPrimitive.Root
4
+
5
+ export { AspectRatio }
src/components/ui/avatar.tsx ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as AvatarPrimitive from "@radix-ui/react-avatar"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const Avatar = React.forwardRef<
7
+ React.ElementRef<typeof AvatarPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
9
+ >(({ className, ...props }, ref) => (
10
+ <AvatarPrimitive.Root
11
+ ref={ref}
12
+ className={cn(
13
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ ))
19
+ Avatar.displayName = AvatarPrimitive.Root.displayName
20
+
21
+ const AvatarImage = React.forwardRef<
22
+ React.ElementRef<typeof AvatarPrimitive.Image>,
23
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
24
+ >(({ className, ...props }, ref) => (
25
+ <AvatarPrimitive.Image
26
+ ref={ref}
27
+ className={cn("aspect-square h-full w-full", className)}
28
+ {...props}
29
+ />
30
+ ))
31
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName
32
+
33
+ const AvatarFallback = React.forwardRef<
34
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
35
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
36
+ >(({ className, ...props }, ref) => (
37
+ <AvatarPrimitive.Fallback
38
+ ref={ref}
39
+ className={cn(
40
+ "flex h-full w-full items-center justify-center rounded-full bg-muted",
41
+ className
42
+ )}
43
+ {...props}
44
+ />
45
+ ))
46
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
47
+
48
+ export { Avatar, AvatarImage, AvatarFallback }
src/components/ui/badge.tsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
13
+ secondary:
14
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
+ destructive:
16
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
17
+ outline: "text-foreground",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: "default",
22
+ },
23
+ }
24
+ )
25
+
26
+ export interface BadgeProps
27
+ extends React.HTMLAttributes<HTMLDivElement>,
28
+ VariantProps<typeof badgeVariants> {}
29
+
30
+ function Badge({ className, variant, ...props }: BadgeProps) {
31
+ return (
32
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
+ )
34
+ }
35
+
36
+ export { Badge, badgeVariants }
src/components/ui/breadcrumb.tsx ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { ChevronRight, MoreHorizontal } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const Breadcrumb = React.forwardRef<
8
+ HTMLElement,
9
+ React.ComponentPropsWithoutRef<"nav"> & {
10
+ separator?: React.ReactNode
11
+ }
12
+ >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
13
+ Breadcrumb.displayName = "Breadcrumb"
14
+
15
+ const BreadcrumbList = React.forwardRef<
16
+ HTMLOListElement,
17
+ React.ComponentPropsWithoutRef<"ol">
18
+ >(({ className, ...props }, ref) => (
19
+ <ol
20
+ ref={ref}
21
+ className={cn(
22
+ "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ ))
28
+ BreadcrumbList.displayName = "BreadcrumbList"
29
+
30
+ const BreadcrumbItem = React.forwardRef<
31
+ HTMLLIElement,
32
+ React.ComponentPropsWithoutRef<"li">
33
+ >(({ className, ...props }, ref) => (
34
+ <li
35
+ ref={ref}
36
+ className={cn("inline-flex items-center gap-1.5", className)}
37
+ {...props}
38
+ />
39
+ ))
40
+ BreadcrumbItem.displayName = "BreadcrumbItem"
41
+
42
+ const BreadcrumbLink = React.forwardRef<
43
+ HTMLAnchorElement,
44
+ React.ComponentPropsWithoutRef<"a"> & {
45
+ asChild?: boolean
46
+ }
47
+ >(({ asChild, className, ...props }, ref) => {
48
+ const Comp = asChild ? Slot : "a"
49
+
50
+ return (
51
+ <Comp
52
+ ref={ref}
53
+ className={cn("transition-colors hover:text-foreground", className)}
54
+ {...props}
55
+ />
56
+ )
57
+ })
58
+ BreadcrumbLink.displayName = "BreadcrumbLink"
59
+
60
+ const BreadcrumbPage = React.forwardRef<
61
+ HTMLSpanElement,
62
+ React.ComponentPropsWithoutRef<"span">
63
+ >(({ className, ...props }, ref) => (
64
+ <span
65
+ ref={ref}
66
+ role="link"
67
+ aria-disabled="true"
68
+ aria-current="page"
69
+ className={cn("font-normal text-foreground", className)}
70
+ {...props}
71
+ />
72
+ ))
73
+ BreadcrumbPage.displayName = "BreadcrumbPage"
74
+
75
+ const BreadcrumbSeparator = ({
76
+ children,
77
+ className,
78
+ ...props
79
+ }: React.ComponentProps<"li">) => (
80
+ <li
81
+ role="presentation"
82
+ aria-hidden="true"
83
+ className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)}
84
+ {...props}
85
+ >
86
+ {children ?? <ChevronRight />}
87
+ </li>
88
+ )
89
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
90
+
91
+ const BreadcrumbEllipsis = ({
92
+ className,
93
+ ...props
94
+ }: React.ComponentProps<"span">) => (
95
+ <span
96
+ role="presentation"
97
+ aria-hidden="true"
98
+ className={cn("flex h-9 w-9 items-center justify-center", className)}
99
+ {...props}
100
+ >
101
+ <MoreHorizontal className="h-4 w-4" />
102
+ <span className="sr-only">More</span>
103
+ </span>
104
+ )
105
+ BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
106
+
107
+ export {
108
+ Breadcrumb,
109
+ BreadcrumbList,
110
+ BreadcrumbItem,
111
+ BreadcrumbLink,
112
+ BreadcrumbPage,
113
+ BreadcrumbSeparator,
114
+ BreadcrumbEllipsis,
115
+ }
src/components/ui/button.tsx ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14
+ destructive:
15
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16
+ outline:
17
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18
+ secondary:
19
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20
+ ghost: "hover:bg-accent hover:text-accent-foreground",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default: "h-9 px-4 py-2",
25
+ sm: "h-8 rounded-md px-3 text-xs",
26
+ lg: "h-10 rounded-md px-8",
27
+ icon: "h-9 w-9",
28
+ },
29
+ },
30
+ defaultVariants: {
31
+ variant: "default",
32
+ size: "default",
33
+ },
34
+ }
35
+ )
36
+
37
+ export interface ButtonProps
38
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39
+ VariantProps<typeof buttonVariants> {
40
+ asChild?: boolean
41
+ }
42
+
43
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
44
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
45
+ const Comp = asChild ? Slot : "button"
46
+ return (
47
+ <Comp
48
+ className={cn(buttonVariants({ variant, size, className }))}
49
+ ref={ref}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+ )
55
+ Button.displayName = "Button"
56
+
57
+ export { Button, buttonVariants }
src/components/ui/calendar.tsx ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import {
3
+ ChevronDownIcon,
4
+ ChevronLeftIcon,
5
+ ChevronRightIcon,
6
+ } from "lucide-react"
7
+ import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
8
+
9
+ import { cn } from "@/lib/utils"
10
+ import { Button, buttonVariants } from "@/components/ui/button"
11
+
12
+ function Calendar({
13
+ className,
14
+ classNames,
15
+ showOutsideDays = true,
16
+ captionLayout = "label",
17
+ buttonVariant = "ghost",
18
+ formatters,
19
+ components,
20
+ ...props
21
+ }: React.ComponentProps<typeof DayPicker> & {
22
+ buttonVariant?: React.ComponentProps<typeof Button>["variant"]
23
+ }) {
24
+ const defaultClassNames = getDefaultClassNames()
25
+
26
+ return (
27
+ <DayPicker
28
+ showOutsideDays={showOutsideDays}
29
+ className={cn(
30
+ "bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
31
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
32
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
33
+ className
34
+ )}
35
+ captionLayout={captionLayout}
36
+ formatters={{
37
+ formatMonthDropdown: (date) =>
38
+ date.toLocaleString("default", { month: "short" }),
39
+ ...formatters,
40
+ }}
41
+ classNames={{
42
+ root: cn("w-fit", defaultClassNames.root),
43
+ months: cn(
44
+ "relative flex flex-col gap-4 md:flex-row",
45
+ defaultClassNames.months
46
+ ),
47
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
48
+ nav: cn(
49
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
50
+ defaultClassNames.nav
51
+ ),
52
+ button_previous: cn(
53
+ buttonVariants({ variant: buttonVariant }),
54
+ "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50",
55
+ defaultClassNames.button_previous
56
+ ),
57
+ button_next: cn(
58
+ buttonVariants({ variant: buttonVariant }),
59
+ "h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50",
60
+ defaultClassNames.button_next
61
+ ),
62
+ month_caption: cn(
63
+ "flex h-[--cell-size] w-full items-center justify-center px-[--cell-size]",
64
+ defaultClassNames.month_caption
65
+ ),
66
+ dropdowns: cn(
67
+ "flex h-[--cell-size] w-full items-center justify-center gap-1.5 text-sm font-medium",
68
+ defaultClassNames.dropdowns
69
+ ),
70
+ dropdown_root: cn(
71
+ "has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-md border",
72
+ defaultClassNames.dropdown_root
73
+ ),
74
+ dropdown: cn(
75
+ "bg-popover absolute inset-0 opacity-0",
76
+ defaultClassNames.dropdown
77
+ ),
78
+ caption_label: cn(
79
+ "select-none font-medium",
80
+ captionLayout === "label"
81
+ ? "text-sm"
82
+ : "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5",
83
+ defaultClassNames.caption_label
84
+ ),
85
+ table: "w-full border-collapse",
86
+ weekdays: cn("flex", defaultClassNames.weekdays),
87
+ weekday: cn(
88
+ "text-muted-foreground flex-1 select-none rounded-md text-[0.8rem] font-normal",
89
+ defaultClassNames.weekday
90
+ ),
91
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
92
+ week_number_header: cn(
93
+ "w-[--cell-size] select-none",
94
+ defaultClassNames.week_number_header
95
+ ),
96
+ week_number: cn(
97
+ "text-muted-foreground select-none text-[0.8rem]",
98
+ defaultClassNames.week_number
99
+ ),
100
+ day: cn(
101
+ "group/day relative aspect-square h-full w-full select-none p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md",
102
+ defaultClassNames.day
103
+ ),
104
+ range_start: cn(
105
+ "bg-accent rounded-l-md",
106
+ defaultClassNames.range_start
107
+ ),
108
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
109
+ range_end: cn("bg-accent rounded-r-md", defaultClassNames.range_end),
110
+ today: cn(
111
+ "bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
112
+ defaultClassNames.today
113
+ ),
114
+ outside: cn(
115
+ "text-muted-foreground aria-selected:text-muted-foreground",
116
+ defaultClassNames.outside
117
+ ),
118
+ disabled: cn(
119
+ "text-muted-foreground opacity-50",
120
+ defaultClassNames.disabled
121
+ ),
122
+ hidden: cn("invisible", defaultClassNames.hidden),
123
+ ...classNames,
124
+ }}
125
+ components={{
126
+ Root: ({ className, rootRef, ...props }) => {
127
+ return (
128
+ <div
129
+ data-slot="calendar"
130
+ ref={rootRef}
131
+ className={cn(className)}
132
+ {...props}
133
+ />
134
+ )
135
+ },
136
+ Chevron: ({ className, orientation, ...props }) => {
137
+ if (orientation === "left") {
138
+ return (
139
+ <ChevronLeftIcon className={cn("size-4", className)} {...props} />
140
+ )
141
+ }
142
+
143
+ if (orientation === "right") {
144
+ return (
145
+ <ChevronRightIcon
146
+ className={cn("size-4", className)}
147
+ {...props}
148
+ />
149
+ )
150
+ }
151
+
152
+ return (
153
+ <ChevronDownIcon className={cn("size-4", className)} {...props} />
154
+ )
155
+ },
156
+ DayButton: CalendarDayButton,
157
+ WeekNumber: ({ children, ...props }) => {
158
+ return (
159
+ <td {...props}>
160
+ <div className="flex size-[--cell-size] items-center justify-center text-center">
161
+ {children}
162
+ </div>
163
+ </td>
164
+ )
165
+ },
166
+ ...components,
167
+ }}
168
+ {...props}
169
+ />
170
+ )
171
+ }
172
+
173
+ function CalendarDayButton({
174
+ className,
175
+ day,
176
+ modifiers,
177
+ ...props
178
+ }: React.ComponentProps<typeof DayButton>) {
179
+ const defaultClassNames = getDefaultClassNames()
180
+
181
+ const ref = React.useRef<HTMLButtonElement>(null)
182
+ React.useEffect(() => {
183
+ if (modifiers.focused) ref.current?.focus()
184
+ }, [modifiers.focused])
185
+
186
+ return (
187
+ <Button
188
+ ref={ref}
189
+ variant="ghost"
190
+ size="icon"
191
+ data-day={day.date.toLocaleDateString()}
192
+ data-selected-single={
193
+ modifiers.selected &&
194
+ !modifiers.range_start &&
195
+ !modifiers.range_end &&
196
+ !modifiers.range_middle
197
+ }
198
+ data-range-start={modifiers.range_start}
199
+ data-range-end={modifiers.range_end}
200
+ data-range-middle={modifiers.range_middle}
201
+ className={cn(
202
+ "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70",
203
+ defaultClassNames.day,
204
+ className
205
+ )}
206
+ {...props}
207
+ />
208
+ )
209
+ }
210
+
211
+ export { Calendar, CalendarDayButton }
src/components/ui/card.tsx ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Card = React.forwardRef<
6
+ HTMLDivElement,
7
+ React.HTMLAttributes<HTMLDivElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div
10
+ ref={ref}
11
+ className={cn(
12
+ "rounded-xl border bg-card text-card-foreground shadow",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ CardHeader.displayName = "CardHeader"
31
+
32
+ const CardTitle = React.forwardRef<
33
+ HTMLDivElement,
34
+ React.HTMLAttributes<HTMLDivElement>
35
+ >(({ className, ...props }, ref) => (
36
+ <div
37
+ ref={ref}
38
+ className={cn("font-semibold leading-none tracking-tight", className)}
39
+ {...props}
40
+ />
41
+ ))
42
+ CardTitle.displayName = "CardTitle"
43
+
44
+ const CardDescription = React.forwardRef<
45
+ HTMLDivElement,
46
+ React.HTMLAttributes<HTMLDivElement>
47
+ >(({ className, ...props }, ref) => (
48
+ <div
49
+ ref={ref}
50
+ className={cn("text-sm text-muted-foreground", className)}
51
+ {...props}
52
+ />
53
+ ))
54
+ CardDescription.displayName = "CardDescription"
55
+
56
+ const CardContent = React.forwardRef<
57
+ HTMLDivElement,
58
+ React.HTMLAttributes<HTMLDivElement>
59
+ >(({ className, ...props }, ref) => (
60
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
61
+ ))
62
+ CardContent.displayName = "CardContent"
63
+
64
+ const CardFooter = React.forwardRef<
65
+ HTMLDivElement,
66
+ React.HTMLAttributes<HTMLDivElement>
67
+ >(({ className, ...props }, ref) => (
68
+ <div
69
+ ref={ref}
70
+ className={cn("flex items-center p-6 pt-0", className)}
71
+ {...props}
72
+ />
73
+ ))
74
+ CardFooter.displayName = "CardFooter"
75
+
76
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
src/components/ui/carousel.tsx ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import useEmblaCarousel, {
5
+ type UseEmblaCarouselType,
6
+ } from "embla-carousel-react"
7
+ import { ArrowLeft, ArrowRight } from "lucide-react"
8
+
9
+ import { cn } from "@/lib/utils"
10
+ import { Button } from "@/components/ui/button"
11
+
12
+ type CarouselApi = UseEmblaCarouselType[1]
13
+ type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
14
+ type CarouselOptions = UseCarouselParameters[0]
15
+ type CarouselPlugin = UseCarouselParameters[1]
16
+
17
+ type CarouselProps = {
18
+ opts?: CarouselOptions
19
+ plugins?: CarouselPlugin
20
+ orientation?: "horizontal" | "vertical"
21
+ setApi?: (api: CarouselApi) => void
22
+ }
23
+
24
+ type CarouselContextProps = {
25
+ carouselRef: ReturnType<typeof useEmblaCarousel>[0]
26
+ api: ReturnType<typeof useEmblaCarousel>[1]
27
+ scrollPrev: () => void
28
+ scrollNext: () => void
29
+ canScrollPrev: boolean
30
+ canScrollNext: boolean
31
+ } & CarouselProps
32
+
33
+ const CarouselContext = React.createContext<CarouselContextProps | null>(null)
34
+
35
+ function useCarousel() {
36
+ const context = React.useContext(CarouselContext)
37
+
38
+ if (!context) {
39
+ throw new Error("useCarousel must be used within a <Carousel />")
40
+ }
41
+
42
+ return context
43
+ }
44
+
45
+ const Carousel = React.forwardRef<
46
+ HTMLDivElement,
47
+ React.HTMLAttributes<HTMLDivElement> & CarouselProps
48
+ >(
49
+ (
50
+ {
51
+ orientation = "horizontal",
52
+ opts,
53
+ setApi,
54
+ plugins,
55
+ className,
56
+ children,
57
+ ...props
58
+ },
59
+ ref
60
+ ) => {
61
+ const [carouselRef, api] = useEmblaCarousel(
62
+ {
63
+ ...opts,
64
+ axis: orientation === "horizontal" ? "x" : "y",
65
+ },
66
+ plugins
67
+ )
68
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
69
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
70
+
71
+ const onSelect = React.useCallback((api: CarouselApi) => {
72
+ if (!api) {
73
+ return
74
+ }
75
+
76
+ setCanScrollPrev(api.canScrollPrev())
77
+ setCanScrollNext(api.canScrollNext())
78
+ }, [])
79
+
80
+ const scrollPrev = React.useCallback(() => {
81
+ api?.scrollPrev()
82
+ }, [api])
83
+
84
+ const scrollNext = React.useCallback(() => {
85
+ api?.scrollNext()
86
+ }, [api])
87
+
88
+ const handleKeyDown = React.useCallback(
89
+ (event: React.KeyboardEvent<HTMLDivElement>) => {
90
+ if (event.key === "ArrowLeft") {
91
+ event.preventDefault()
92
+ scrollPrev()
93
+ } else if (event.key === "ArrowRight") {
94
+ event.preventDefault()
95
+ scrollNext()
96
+ }
97
+ },
98
+ [scrollPrev, scrollNext]
99
+ )
100
+
101
+ React.useEffect(() => {
102
+ if (!api || !setApi) {
103
+ return
104
+ }
105
+
106
+ setApi(api)
107
+ }, [api, setApi])
108
+
109
+ React.useEffect(() => {
110
+ if (!api) {
111
+ return
112
+ }
113
+
114
+ onSelect(api)
115
+ api.on("reInit", onSelect)
116
+ api.on("select", onSelect)
117
+
118
+ return () => {
119
+ api?.off("select", onSelect)
120
+ }
121
+ }, [api, onSelect])
122
+
123
+ return (
124
+ <CarouselContext.Provider
125
+ value={{
126
+ carouselRef,
127
+ api: api,
128
+ opts,
129
+ orientation:
130
+ orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
131
+ scrollPrev,
132
+ scrollNext,
133
+ canScrollPrev,
134
+ canScrollNext,
135
+ }}
136
+ >
137
+ <div
138
+ ref={ref}
139
+ onKeyDownCapture={handleKeyDown}
140
+ className={cn("relative", className)}
141
+ role="region"
142
+ aria-roledescription="carousel"
143
+ {...props}
144
+ >
145
+ {children}
146
+ </div>
147
+ </CarouselContext.Provider>
148
+ )
149
+ }
150
+ )
151
+ Carousel.displayName = "Carousel"
152
+
153
+ const CarouselContent = React.forwardRef<
154
+ HTMLDivElement,
155
+ React.HTMLAttributes<HTMLDivElement>
156
+ >(({ className, ...props }, ref) => {
157
+ const { carouselRef, orientation } = useCarousel()
158
+
159
+ return (
160
+ <div ref={carouselRef} className="overflow-hidden">
161
+ <div
162
+ ref={ref}
163
+ className={cn(
164
+ "flex",
165
+ orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
166
+ className
167
+ )}
168
+ {...props}
169
+ />
170
+ </div>
171
+ )
172
+ })
173
+ CarouselContent.displayName = "CarouselContent"
174
+
175
+ const CarouselItem = React.forwardRef<
176
+ HTMLDivElement,
177
+ React.HTMLAttributes<HTMLDivElement>
178
+ >(({ className, ...props }, ref) => {
179
+ const { orientation } = useCarousel()
180
+
181
+ return (
182
+ <div
183
+ ref={ref}
184
+ role="group"
185
+ aria-roledescription="slide"
186
+ className={cn(
187
+ "min-w-0 shrink-0 grow-0 basis-full",
188
+ orientation === "horizontal" ? "pl-4" : "pt-4",
189
+ className
190
+ )}
191
+ {...props}
192
+ />
193
+ )
194
+ })
195
+ CarouselItem.displayName = "CarouselItem"
196
+
197
+ const CarouselPrevious = React.forwardRef<
198
+ HTMLButtonElement,
199
+ React.ComponentProps<typeof Button>
200
+ >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
201
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
202
+
203
+ return (
204
+ <Button
205
+ ref={ref}
206
+ variant={variant}
207
+ size={size}
208
+ className={cn(
209
+ "absolute h-8 w-8 rounded-full",
210
+ orientation === "horizontal"
211
+ ? "-left-12 top-1/2 -translate-y-1/2"
212
+ : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
213
+ className
214
+ )}
215
+ disabled={!canScrollPrev}
216
+ onClick={scrollPrev}
217
+ {...props}
218
+ >
219
+ <ArrowLeft className="h-4 w-4" />
220
+ <span className="sr-only">Previous slide</span>
221
+ </Button>
222
+ )
223
+ })
224
+ CarouselPrevious.displayName = "CarouselPrevious"
225
+
226
+ const CarouselNext = React.forwardRef<
227
+ HTMLButtonElement,
228
+ React.ComponentProps<typeof Button>
229
+ >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
230
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
231
+
232
+ return (
233
+ <Button
234
+ ref={ref}
235
+ variant={variant}
236
+ size={size}
237
+ className={cn(
238
+ "absolute h-8 w-8 rounded-full",
239
+ orientation === "horizontal"
240
+ ? "-right-12 top-1/2 -translate-y-1/2"
241
+ : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
242
+ className
243
+ )}
244
+ disabled={!canScrollNext}
245
+ onClick={scrollNext}
246
+ {...props}
247
+ >
248
+ <ArrowRight className="h-4 w-4" />
249
+ <span className="sr-only">Next slide</span>
250
+ </Button>
251
+ )
252
+ })
253
+ CarouselNext.displayName = "CarouselNext"
254
+
255
+ export {
256
+ type CarouselApi,
257
+ Carousel,
258
+ CarouselContent,
259
+ CarouselItem,
260
+ CarouselPrevious,
261
+ CarouselNext,
262
+ }
src/components/ui/checkbox.tsx ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5
+ import { Check } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const Checkbox = React.forwardRef<
10
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
11
+ React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
12
+ >(({ className, ...props }, ref) => (
13
+ <CheckboxPrimitive.Root
14
+ ref={ref}
15
+ className={cn(
16
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
17
+ className
18
+ )}
19
+ {...props}
20
+ >
21
+ <CheckboxPrimitive.Indicator
22
+ className={cn("flex items-center justify-center text-current")}
23
+ >
24
+ <Check className="h-4 w-4" />
25
+ </CheckboxPrimitive.Indicator>
26
+ </CheckboxPrimitive.Root>
27
+ ))
28
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName
29
+
30
+ export { Checkbox }
src/components/ui/collapsible.tsx ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
2
+
3
+ const Collapsible = CollapsiblePrimitive.Root
4
+
5
+ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
6
+
7
+ const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
8
+
9
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent }
src/components/ui/command.tsx ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { type DialogProps } from "@radix-ui/react-dialog"
5
+ import { Command as CommandPrimitive } from "cmdk"
6
+ import { Search } from "lucide-react"
7
+
8
+ import { cn } from "@/lib/utils"
9
+ import { Dialog, DialogContent } from "@/components/ui/dialog"
10
+
11
+ const Command = React.forwardRef<
12
+ React.ElementRef<typeof CommandPrimitive>,
13
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive>
14
+ >(({ className, ...props }, ref) => (
15
+ <CommandPrimitive
16
+ ref={ref}
17
+ className={cn(
18
+ "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
19
+ className
20
+ )}
21
+ {...props}
22
+ />
23
+ ))
24
+ Command.displayName = CommandPrimitive.displayName
25
+
26
+ const CommandDialog = ({ children, ...props }: DialogProps) => {
27
+ return (
28
+ <Dialog {...props}>
29
+ <DialogContent className="overflow-hidden p-0">
30
+ <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
31
+ {children}
32
+ </Command>
33
+ </DialogContent>
34
+ </Dialog>
35
+ )
36
+ }
37
+
38
+ const CommandInput = React.forwardRef<
39
+ React.ElementRef<typeof CommandPrimitive.Input>,
40
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
41
+ >(({ className, ...props }, ref) => (
42
+ <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
43
+ <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
44
+ <CommandPrimitive.Input
45
+ ref={ref}
46
+ className={cn(
47
+ "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
48
+ className
49
+ )}
50
+ {...props}
51
+ />
52
+ </div>
53
+ ))
54
+
55
+ CommandInput.displayName = CommandPrimitive.Input.displayName
56
+
57
+ const CommandList = React.forwardRef<
58
+ React.ElementRef<typeof CommandPrimitive.List>,
59
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
60
+ >(({ className, ...props }, ref) => (
61
+ <CommandPrimitive.List
62
+ ref={ref}
63
+ className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
64
+ {...props}
65
+ />
66
+ ))
67
+
68
+ CommandList.displayName = CommandPrimitive.List.displayName
69
+
70
+ const CommandEmpty = React.forwardRef<
71
+ React.ElementRef<typeof CommandPrimitive.Empty>,
72
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
73
+ >((props, ref) => (
74
+ <CommandPrimitive.Empty
75
+ ref={ref}
76
+ className="py-6 text-center text-sm"
77
+ {...props}
78
+ />
79
+ ))
80
+
81
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName
82
+
83
+ const CommandGroup = React.forwardRef<
84
+ React.ElementRef<typeof CommandPrimitive.Group>,
85
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
86
+ >(({ className, ...props }, ref) => (
87
+ <CommandPrimitive.Group
88
+ ref={ref}
89
+ className={cn(
90
+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
91
+ className
92
+ )}
93
+ {...props}
94
+ />
95
+ ))
96
+
97
+ CommandGroup.displayName = CommandPrimitive.Group.displayName
98
+
99
+ const CommandSeparator = React.forwardRef<
100
+ React.ElementRef<typeof CommandPrimitive.Separator>,
101
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
102
+ >(({ className, ...props }, ref) => (
103
+ <CommandPrimitive.Separator
104
+ ref={ref}
105
+ className={cn("-mx-1 h-px bg-border", className)}
106
+ {...props}
107
+ />
108
+ ))
109
+ CommandSeparator.displayName = CommandPrimitive.Separator.displayName
110
+
111
+ const CommandItem = React.forwardRef<
112
+ React.ElementRef<typeof CommandPrimitive.Item>,
113
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
114
+ >(({ className, ...props }, ref) => (
115
+ <CommandPrimitive.Item
116
+ ref={ref}
117
+ className={cn(
118
+ "relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
119
+ className
120
+ )}
121
+ {...props}
122
+ />
123
+ ))
124
+
125
+ CommandItem.displayName = CommandPrimitive.Item.displayName
126
+
127
+ const CommandShortcut = ({
128
+ className,
129
+ ...props
130
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
131
+ return (
132
+ <span
133
+ className={cn(
134
+ "ml-auto text-xs tracking-widest text-muted-foreground",
135
+ className
136
+ )}
137
+ {...props}
138
+ />
139
+ )
140
+ }
141
+ CommandShortcut.displayName = "CommandShortcut"
142
+
143
+ export {
144
+ Command,
145
+ CommandDialog,
146
+ CommandInput,
147
+ CommandList,
148
+ CommandEmpty,
149
+ CommandGroup,
150
+ CommandItem,
151
+ CommandShortcut,
152
+ CommandSeparator,
153
+ }
src/components/ui/context-menu.tsx ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
5
+ import { Check, ChevronRight, Circle } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const ContextMenu = ContextMenuPrimitive.Root
10
+
11
+ const ContextMenuTrigger = ContextMenuPrimitive.Trigger
12
+
13
+ const ContextMenuGroup = ContextMenuPrimitive.Group
14
+
15
+ const ContextMenuPortal = ContextMenuPrimitive.Portal
16
+
17
+ const ContextMenuSub = ContextMenuPrimitive.Sub
18
+
19
+ const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
20
+
21
+ const ContextMenuSubTrigger = React.forwardRef<
22
+ React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
23
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
24
+ inset?: boolean
25
+ }
26
+ >(({ className, inset, children, ...props }, ref) => (
27
+ <ContextMenuPrimitive.SubTrigger
28
+ ref={ref}
29
+ className={cn(
30
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
31
+ inset && "pl-8",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <ChevronRight className="ml-auto h-4 w-4" />
38
+ </ContextMenuPrimitive.SubTrigger>
39
+ ))
40
+ ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
41
+
42
+ const ContextMenuSubContent = React.forwardRef<
43
+ React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
44
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
45
+ >(({ className, ...props }, ref) => (
46
+ <ContextMenuPrimitive.SubContent
47
+ ref={ref}
48
+ className={cn(
49
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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 origin-[--radix-context-menu-content-transform-origin]",
50
+ className
51
+ )}
52
+ {...props}
53
+ />
54
+ ))
55
+ ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
56
+
57
+ const ContextMenuContent = React.forwardRef<
58
+ React.ElementRef<typeof ContextMenuPrimitive.Content>,
59
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
60
+ >(({ className, ...props }, ref) => (
61
+ <ContextMenuPrimitive.Portal>
62
+ <ContextMenuPrimitive.Content
63
+ ref={ref}
64
+ className={cn(
65
+ "z-50 max-h-[--radix-context-menu-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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 origin-[--radix-context-menu-content-transform-origin]",
66
+ className
67
+ )}
68
+ {...props}
69
+ />
70
+ </ContextMenuPrimitive.Portal>
71
+ ))
72
+ ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
73
+
74
+ const ContextMenuItem = React.forwardRef<
75
+ React.ElementRef<typeof ContextMenuPrimitive.Item>,
76
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
77
+ inset?: boolean
78
+ }
79
+ >(({ className, inset, ...props }, ref) => (
80
+ <ContextMenuPrimitive.Item
81
+ ref={ref}
82
+ className={cn(
83
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
84
+ inset && "pl-8",
85
+ className
86
+ )}
87
+ {...props}
88
+ />
89
+ ))
90
+ ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
91
+
92
+ const ContextMenuCheckboxItem = React.forwardRef<
93
+ React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
94
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
95
+ >(({ className, children, checked, ...props }, ref) => (
96
+ <ContextMenuPrimitive.CheckboxItem
97
+ ref={ref}
98
+ className={cn(
99
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
100
+ className
101
+ )}
102
+ checked={checked}
103
+ {...props}
104
+ >
105
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
106
+ <ContextMenuPrimitive.ItemIndicator>
107
+ <Check className="h-4 w-4" />
108
+ </ContextMenuPrimitive.ItemIndicator>
109
+ </span>
110
+ {children}
111
+ </ContextMenuPrimitive.CheckboxItem>
112
+ ))
113
+ ContextMenuCheckboxItem.displayName =
114
+ ContextMenuPrimitive.CheckboxItem.displayName
115
+
116
+ const ContextMenuRadioItem = React.forwardRef<
117
+ React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
118
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
119
+ >(({ className, children, ...props }, ref) => (
120
+ <ContextMenuPrimitive.RadioItem
121
+ ref={ref}
122
+ className={cn(
123
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
124
+ className
125
+ )}
126
+ {...props}
127
+ >
128
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
129
+ <ContextMenuPrimitive.ItemIndicator>
130
+ <Circle className="h-4 w-4 fill-current" />
131
+ </ContextMenuPrimitive.ItemIndicator>
132
+ </span>
133
+ {children}
134
+ </ContextMenuPrimitive.RadioItem>
135
+ ))
136
+ ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
137
+
138
+ const ContextMenuLabel = React.forwardRef<
139
+ React.ElementRef<typeof ContextMenuPrimitive.Label>,
140
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
141
+ inset?: boolean
142
+ }
143
+ >(({ className, inset, ...props }, ref) => (
144
+ <ContextMenuPrimitive.Label
145
+ ref={ref}
146
+ className={cn(
147
+ "px-2 py-1.5 text-sm font-semibold text-foreground",
148
+ inset && "pl-8",
149
+ className
150
+ )}
151
+ {...props}
152
+ />
153
+ ))
154
+ ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
155
+
156
+ const ContextMenuSeparator = React.forwardRef<
157
+ React.ElementRef<typeof ContextMenuPrimitive.Separator>,
158
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
159
+ >(({ className, ...props }, ref) => (
160
+ <ContextMenuPrimitive.Separator
161
+ ref={ref}
162
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
163
+ {...props}
164
+ />
165
+ ))
166
+ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
167
+
168
+ const ContextMenuShortcut = ({
169
+ className,
170
+ ...props
171
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
172
+ return (
173
+ <span
174
+ className={cn(
175
+ "ml-auto text-xs tracking-widest text-muted-foreground",
176
+ className
177
+ )}
178
+ {...props}
179
+ />
180
+ )
181
+ }
182
+ ContextMenuShortcut.displayName = "ContextMenuShortcut"
183
+
184
+ export {
185
+ ContextMenu,
186
+ ContextMenuTrigger,
187
+ ContextMenuContent,
188
+ ContextMenuItem,
189
+ ContextMenuCheckboxItem,
190
+ ContextMenuRadioItem,
191
+ ContextMenuLabel,
192
+ ContextMenuSeparator,
193
+ ContextMenuShortcut,
194
+ ContextMenuGroup,
195
+ ContextMenuPortal,
196
+ ContextMenuSub,
197
+ ContextMenuSubContent,
198
+ ContextMenuSubTrigger,
199
+ ContextMenuRadioGroup,
200
+ }
src/components/ui/dialog.tsx ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog"
3
+ import { X } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const Dialog = DialogPrimitive.Root
8
+
9
+ const DialogTrigger = DialogPrimitive.Trigger
10
+
11
+ const DialogPortal = DialogPrimitive.Portal
12
+
13
+ const DialogClose = DialogPrimitive.Close
14
+
15
+ const DialogOverlay = React.forwardRef<
16
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
17
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
18
+ >(({ className, ...props }, ref) => (
19
+ <DialogPrimitive.Overlay
20
+ ref={ref}
21
+ className={cn(
22
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ ))
28
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
29
+
30
+ const DialogContent = React.forwardRef<
31
+ React.ElementRef<typeof DialogPrimitive.Content>,
32
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
33
+ >(({ className, children, ...props }, ref) => (
34
+ <DialogPortal>
35
+ <DialogOverlay />
36
+ <DialogPrimitive.Content
37
+ ref={ref}
38
+ className={cn(
39
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
40
+ className
41
+ )}
42
+ {...props}
43
+ >
44
+ {children}
45
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
46
+ <X className="h-4 w-4" />
47
+ <span className="sr-only">Close</span>
48
+ </DialogPrimitive.Close>
49
+ </DialogPrimitive.Content>
50
+ </DialogPortal>
51
+ ))
52
+ DialogContent.displayName = DialogPrimitive.Content.displayName
53
+
54
+ const DialogHeader = ({
55
+ className,
56
+ ...props
57
+ }: React.HTMLAttributes<HTMLDivElement>) => (
58
+ <div
59
+ className={cn(
60
+ "flex flex-col space-y-1.5 text-center sm:text-left",
61
+ className
62
+ )}
63
+ {...props}
64
+ />
65
+ )
66
+ DialogHeader.displayName = "DialogHeader"
67
+
68
+ const DialogFooter = ({
69
+ className,
70
+ ...props
71
+ }: React.HTMLAttributes<HTMLDivElement>) => (
72
+ <div
73
+ className={cn(
74
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
75
+ className
76
+ )}
77
+ {...props}
78
+ />
79
+ )
80
+ DialogFooter.displayName = "DialogFooter"
81
+
82
+ const DialogTitle = React.forwardRef<
83
+ React.ElementRef<typeof DialogPrimitive.Title>,
84
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
85
+ >(({ className, ...props }, ref) => (
86
+ <DialogPrimitive.Title
87
+ ref={ref}
88
+ className={cn(
89
+ "text-lg font-semibold leading-none tracking-tight",
90
+ className
91
+ )}
92
+ {...props}
93
+ />
94
+ ))
95
+ DialogTitle.displayName = DialogPrimitive.Title.displayName
96
+
97
+ const DialogDescription = React.forwardRef<
98
+ React.ElementRef<typeof DialogPrimitive.Description>,
99
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
100
+ >(({ className, ...props }, ref) => (
101
+ <DialogPrimitive.Description
102
+ ref={ref}
103
+ className={cn("text-sm text-muted-foreground", className)}
104
+ {...props}
105
+ />
106
+ ))
107
+ DialogDescription.displayName = DialogPrimitive.Description.displayName
108
+
109
+ export {
110
+ Dialog,
111
+ DialogPortal,
112
+ DialogOverlay,
113
+ DialogTrigger,
114
+ DialogClose,
115
+ DialogContent,
116
+ DialogHeader,
117
+ DialogFooter,
118
+ DialogTitle,
119
+ DialogDescription,
120
+ }
src/components/ui/drawer.tsx ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Drawer as DrawerPrimitive } from "vaul"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Drawer = ({
9
+ shouldScaleBackground = true,
10
+ ...props
11
+ }: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
12
+ <DrawerPrimitive.Root
13
+ shouldScaleBackground={shouldScaleBackground}
14
+ {...props}
15
+ />
16
+ )
17
+ Drawer.displayName = "Drawer"
18
+
19
+ const DrawerTrigger = DrawerPrimitive.Trigger
20
+
21
+ const DrawerPortal = DrawerPrimitive.Portal
22
+
23
+ const DrawerClose = DrawerPrimitive.Close
24
+
25
+ const DrawerOverlay = React.forwardRef<
26
+ React.ElementRef<typeof DrawerPrimitive.Overlay>,
27
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
28
+ >(({ className, ...props }, ref) => (
29
+ <DrawerPrimitive.Overlay
30
+ ref={ref}
31
+ className={cn("fixed inset-0 z-50 bg-black/80", className)}
32
+ {...props}
33
+ />
34
+ ))
35
+ DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
36
+
37
+ const DrawerContent = React.forwardRef<
38
+ React.ElementRef<typeof DrawerPrimitive.Content>,
39
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
40
+ >(({ className, children, ...props }, ref) => (
41
+ <DrawerPortal>
42
+ <DrawerOverlay />
43
+ <DrawerPrimitive.Content
44
+ ref={ref}
45
+ className={cn(
46
+ "fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
47
+ className
48
+ )}
49
+ {...props}
50
+ >
51
+ <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
52
+ {children}
53
+ </DrawerPrimitive.Content>
54
+ </DrawerPortal>
55
+ ))
56
+ DrawerContent.displayName = "DrawerContent"
57
+
58
+ const DrawerHeader = ({
59
+ className,
60
+ ...props
61
+ }: React.HTMLAttributes<HTMLDivElement>) => (
62
+ <div
63
+ className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
64
+ {...props}
65
+ />
66
+ )
67
+ DrawerHeader.displayName = "DrawerHeader"
68
+
69
+ const DrawerFooter = ({
70
+ className,
71
+ ...props
72
+ }: React.HTMLAttributes<HTMLDivElement>) => (
73
+ <div
74
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
75
+ {...props}
76
+ />
77
+ )
78
+ DrawerFooter.displayName = "DrawerFooter"
79
+
80
+ const DrawerTitle = React.forwardRef<
81
+ React.ElementRef<typeof DrawerPrimitive.Title>,
82
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
83
+ >(({ className, ...props }, ref) => (
84
+ <DrawerPrimitive.Title
85
+ ref={ref}
86
+ className={cn(
87
+ "text-lg font-semibold leading-none tracking-tight",
88
+ className
89
+ )}
90
+ {...props}
91
+ />
92
+ ))
93
+ DrawerTitle.displayName = DrawerPrimitive.Title.displayName
94
+
95
+ const DrawerDescription = React.forwardRef<
96
+ React.ElementRef<typeof DrawerPrimitive.Description>,
97
+ React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
98
+ >(({ className, ...props }, ref) => (
99
+ <DrawerPrimitive.Description
100
+ ref={ref}
101
+ className={cn("text-sm text-muted-foreground", className)}
102
+ {...props}
103
+ />
104
+ ))
105
+ DrawerDescription.displayName = DrawerPrimitive.Description.displayName
106
+
107
+ export {
108
+ Drawer,
109
+ DrawerPortal,
110
+ DrawerOverlay,
111
+ DrawerTrigger,
112
+ DrawerClose,
113
+ DrawerContent,
114
+ DrawerHeader,
115
+ DrawerFooter,
116
+ DrawerTitle,
117
+ DrawerDescription,
118
+ }
src/components/ui/dropdown-menu.tsx ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
5
+ import { Check, ChevronRight, Circle } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const DropdownMenu = DropdownMenuPrimitive.Root
10
+
11
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
12
+
13
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group
14
+
15
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal
16
+
17
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub
18
+
19
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
20
+
21
+ const DropdownMenuSubTrigger = React.forwardRef<
22
+ React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
23
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
24
+ inset?: boolean
25
+ }
26
+ >(({ className, inset, children, ...props }, ref) => (
27
+ <DropdownMenuPrimitive.SubTrigger
28
+ ref={ref}
29
+ className={cn(
30
+ "flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
31
+ inset && "pl-8",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <ChevronRight className="ml-auto" />
38
+ </DropdownMenuPrimitive.SubTrigger>
39
+ ))
40
+ DropdownMenuSubTrigger.displayName =
41
+ DropdownMenuPrimitive.SubTrigger.displayName
42
+
43
+ const DropdownMenuSubContent = React.forwardRef<
44
+ React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
45
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
46
+ >(({ className, ...props }, ref) => (
47
+ <DropdownMenuPrimitive.SubContent
48
+ ref={ref}
49
+ className={cn(
50
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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 origin-[--radix-dropdown-menu-content-transform-origin]",
51
+ className
52
+ )}
53
+ {...props}
54
+ />
55
+ ))
56
+ DropdownMenuSubContent.displayName =
57
+ DropdownMenuPrimitive.SubContent.displayName
58
+
59
+ const DropdownMenuContent = React.forwardRef<
60
+ React.ElementRef<typeof DropdownMenuPrimitive.Content>,
61
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
62
+ >(({ className, sideOffset = 4, ...props }, ref) => (
63
+ <DropdownMenuPrimitive.Portal>
64
+ <DropdownMenuPrimitive.Content
65
+ ref={ref}
66
+ sideOffset={sideOffset}
67
+ className={cn(
68
+ "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
69
+ "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 origin-[--radix-dropdown-menu-content-transform-origin]",
70
+ className
71
+ )}
72
+ {...props}
73
+ />
74
+ </DropdownMenuPrimitive.Portal>
75
+ ))
76
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
77
+
78
+ const DropdownMenuItem = React.forwardRef<
79
+ React.ElementRef<typeof DropdownMenuPrimitive.Item>,
80
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
81
+ inset?: boolean
82
+ }
83
+ >(({ className, inset, ...props }, ref) => (
84
+ <DropdownMenuPrimitive.Item
85
+ ref={ref}
86
+ className={cn(
87
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
88
+ inset && "pl-8",
89
+ className
90
+ )}
91
+ {...props}
92
+ />
93
+ ))
94
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
95
+
96
+ const DropdownMenuCheckboxItem = React.forwardRef<
97
+ React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
98
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
99
+ >(({ className, children, checked, ...props }, ref) => (
100
+ <DropdownMenuPrimitive.CheckboxItem
101
+ ref={ref}
102
+ className={cn(
103
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
104
+ className
105
+ )}
106
+ checked={checked}
107
+ {...props}
108
+ >
109
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
110
+ <DropdownMenuPrimitive.ItemIndicator>
111
+ <Check className="h-4 w-4" />
112
+ </DropdownMenuPrimitive.ItemIndicator>
113
+ </span>
114
+ {children}
115
+ </DropdownMenuPrimitive.CheckboxItem>
116
+ ))
117
+ DropdownMenuCheckboxItem.displayName =
118
+ DropdownMenuPrimitive.CheckboxItem.displayName
119
+
120
+ const DropdownMenuRadioItem = React.forwardRef<
121
+ React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
122
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
123
+ >(({ className, children, ...props }, ref) => (
124
+ <DropdownMenuPrimitive.RadioItem
125
+ ref={ref}
126
+ className={cn(
127
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
128
+ className
129
+ )}
130
+ {...props}
131
+ >
132
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
133
+ <DropdownMenuPrimitive.ItemIndicator>
134
+ <Circle className="h-2 w-2 fill-current" />
135
+ </DropdownMenuPrimitive.ItemIndicator>
136
+ </span>
137
+ {children}
138
+ </DropdownMenuPrimitive.RadioItem>
139
+ ))
140
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
141
+
142
+ const DropdownMenuLabel = React.forwardRef<
143
+ React.ElementRef<typeof DropdownMenuPrimitive.Label>,
144
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
145
+ inset?: boolean
146
+ }
147
+ >(({ className, inset, ...props }, ref) => (
148
+ <DropdownMenuPrimitive.Label
149
+ ref={ref}
150
+ className={cn(
151
+ "px-2 py-1.5 text-sm font-semibold",
152
+ inset && "pl-8",
153
+ className
154
+ )}
155
+ {...props}
156
+ />
157
+ ))
158
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
159
+
160
+ const DropdownMenuSeparator = React.forwardRef<
161
+ React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
162
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
163
+ >(({ className, ...props }, ref) => (
164
+ <DropdownMenuPrimitive.Separator
165
+ ref={ref}
166
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
167
+ {...props}
168
+ />
169
+ ))
170
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
171
+
172
+ const DropdownMenuShortcut = ({
173
+ className,
174
+ ...props
175
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
176
+ return (
177
+ <span
178
+ className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
179
+ {...props}
180
+ />
181
+ )
182
+ }
183
+ DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
184
+
185
+ export {
186
+ DropdownMenu,
187
+ DropdownMenuTrigger,
188
+ DropdownMenuContent,
189
+ DropdownMenuItem,
190
+ DropdownMenuCheckboxItem,
191
+ DropdownMenuRadioItem,
192
+ DropdownMenuLabel,
193
+ DropdownMenuSeparator,
194
+ DropdownMenuShortcut,
195
+ DropdownMenuGroup,
196
+ DropdownMenuPortal,
197
+ DropdownMenuSub,
198
+ DropdownMenuSubContent,
199
+ DropdownMenuSubTrigger,
200
+ DropdownMenuRadioGroup,
201
+ }
src/components/ui/form.tsx ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as LabelPrimitive from "@radix-ui/react-label"
3
+ import { Slot } from "@radix-ui/react-slot"
4
+ import {
5
+ Controller,
6
+ FormProvider,
7
+ useFormContext,
8
+ type ControllerProps,
9
+ type FieldPath,
10
+ type FieldValues,
11
+ } from "react-hook-form"
12
+
13
+ import { cn } from "@/lib/utils"
14
+ import { Label } from "@/components/ui/label"
15
+
16
+ const Form = FormProvider
17
+
18
+ type FormFieldContextValue<
19
+ TFieldValues extends FieldValues = FieldValues,
20
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
21
+ > = {
22
+ name: TName
23
+ }
24
+
25
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
26
+ {} as FormFieldContextValue
27
+ )
28
+
29
+ const FormField = <
30
+ TFieldValues extends FieldValues = FieldValues,
31
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
32
+ >({
33
+ ...props
34
+ }: ControllerProps<TFieldValues, TName>) => {
35
+ return (
36
+ <FormFieldContext.Provider value={{ name: props.name }}>
37
+ <Controller {...props} />
38
+ </FormFieldContext.Provider>
39
+ )
40
+ }
41
+
42
+ const useFormField = () => {
43
+ const fieldContext = React.useContext(FormFieldContext)
44
+ const itemContext = React.useContext(FormItemContext)
45
+ const { getFieldState, formState } = useFormContext()
46
+
47
+ const fieldState = getFieldState(fieldContext.name, formState)
48
+
49
+ if (!fieldContext) {
50
+ throw new Error("useFormField should be used within <FormField>")
51
+ }
52
+
53
+ const { id } = itemContext
54
+
55
+ return {
56
+ id,
57
+ name: fieldContext.name,
58
+ formItemId: `${id}-form-item`,
59
+ formDescriptionId: `${id}-form-item-description`,
60
+ formMessageId: `${id}-form-item-message`,
61
+ ...fieldState,
62
+ }
63
+ }
64
+
65
+ type FormItemContextValue = {
66
+ id: string
67
+ }
68
+
69
+ const FormItemContext = React.createContext<FormItemContextValue>(
70
+ {} as FormItemContextValue
71
+ )
72
+
73
+ const FormItem = React.forwardRef<
74
+ HTMLDivElement,
75
+ React.HTMLAttributes<HTMLDivElement>
76
+ >(({ className, ...props }, ref) => {
77
+ const id = React.useId()
78
+
79
+ return (
80
+ <FormItemContext.Provider value={{ id }}>
81
+ <div ref={ref} className={cn("space-y-2", className)} {...props} />
82
+ </FormItemContext.Provider>
83
+ )
84
+ })
85
+ FormItem.displayName = "FormItem"
86
+
87
+ const FormLabel = React.forwardRef<
88
+ React.ElementRef<typeof LabelPrimitive.Root>,
89
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
90
+ >(({ className, ...props }, ref) => {
91
+ const { error, formItemId } = useFormField()
92
+
93
+ return (
94
+ <Label
95
+ ref={ref}
96
+ className={cn(error && "text-destructive", className)}
97
+ htmlFor={formItemId}
98
+ {...props}
99
+ />
100
+ )
101
+ })
102
+ FormLabel.displayName = "FormLabel"
103
+
104
+ const FormControl = React.forwardRef<
105
+ React.ElementRef<typeof Slot>,
106
+ React.ComponentPropsWithoutRef<typeof Slot>
107
+ >(({ ...props }, ref) => {
108
+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
109
+
110
+ return (
111
+ <Slot
112
+ ref={ref}
113
+ id={formItemId}
114
+ aria-describedby={
115
+ !error
116
+ ? `${formDescriptionId}`
117
+ : `${formDescriptionId} ${formMessageId}`
118
+ }
119
+ aria-invalid={!!error}
120
+ {...props}
121
+ />
122
+ )
123
+ })
124
+ FormControl.displayName = "FormControl"
125
+
126
+ const FormDescription = React.forwardRef<
127
+ HTMLParagraphElement,
128
+ React.HTMLAttributes<HTMLParagraphElement>
129
+ >(({ className, ...props }, ref) => {
130
+ const { formDescriptionId } = useFormField()
131
+
132
+ return (
133
+ <p
134
+ ref={ref}
135
+ id={formDescriptionId}
136
+ className={cn("text-[0.8rem] text-muted-foreground", className)}
137
+ {...props}
138
+ />
139
+ )
140
+ })
141
+ FormDescription.displayName = "FormDescription"
142
+
143
+ const FormMessage = React.forwardRef<
144
+ HTMLParagraphElement,
145
+ React.HTMLAttributes<HTMLParagraphElement>
146
+ >(({ className, children, ...props }, ref) => {
147
+ const { error, formMessageId } = useFormField()
148
+ const body = error ? String(error?.message ?? "") : children
149
+
150
+ if (!body) {
151
+ return null
152
+ }
153
+
154
+ return (
155
+ <p
156
+ ref={ref}
157
+ id={formMessageId}
158
+ className={cn("text-[0.8rem] font-medium text-destructive", className)}
159
+ {...props}
160
+ >
161
+ {body}
162
+ </p>
163
+ )
164
+ })
165
+ FormMessage.displayName = "FormMessage"
166
+
167
+ export {
168
+ useFormField,
169
+ Form,
170
+ FormItem,
171
+ FormLabel,
172
+ FormControl,
173
+ FormDescription,
174
+ FormMessage,
175
+ FormField,
176
+ }
src/components/ui/hover-card.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const HoverCard = HoverCardPrimitive.Root
9
+
10
+ const HoverCardTrigger = HoverCardPrimitive.Trigger
11
+
12
+ const HoverCardContent = React.forwardRef<
13
+ React.ElementRef<typeof HoverCardPrimitive.Content>,
14
+ React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
15
+ >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16
+ <HoverCardPrimitive.Content
17
+ ref={ref}
18
+ align={align}
19
+ sideOffset={sideOffset}
20
+ className={cn(
21
+ "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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 origin-[--radix-hover-card-content-transform-origin]",
22
+ className
23
+ )}
24
+ {...props}
25
+ />
26
+ ))
27
+ HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
28
+
29
+ export { HoverCard, HoverCardTrigger, HoverCardContent }
src/components/ui/input.tsx ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
6
+ ({ className, type, ...props }, ref) => {
7
+ return (
8
+ <input
9
+ type={type}
10
+ className={cn(
11
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-[color,box-shadow] file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:border-[var(--fg-base)] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
+ className
13
+ )}
14
+ ref={ref}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+ )
20
+ Input.displayName = "Input"
21
+
22
+ export { Input }
src/components/ui/label.tsx ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as LabelPrimitive from "@radix-ui/react-label"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ const labelVariants = cva(
10
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11
+ )
12
+
13
+ const Label = React.forwardRef<
14
+ React.ElementRef<typeof LabelPrimitive.Root>,
15
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
16
+ VariantProps<typeof labelVariants>
17
+ >(({ className, ...props }, ref) => (
18
+ <LabelPrimitive.Root
19
+ ref={ref}
20
+ className={cn(labelVariants(), className)}
21
+ {...props}
22
+ />
23
+ ))
24
+ Label.displayName = LabelPrimitive.Root.displayName
25
+
26
+ export { Label }
src/components/ui/menubar.tsx ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as MenubarPrimitive from "@radix-ui/react-menubar"
5
+ import { Check, ChevronRight, Circle } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ function MenubarMenu({
10
+ ...props
11
+ }: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
12
+ return <MenubarPrimitive.Menu {...props} />
13
+ }
14
+
15
+ function MenubarGroup({
16
+ ...props
17
+ }: React.ComponentProps<typeof MenubarPrimitive.Group>) {
18
+ return <MenubarPrimitive.Group {...props} />
19
+ }
20
+
21
+ function MenubarPortal({
22
+ ...props
23
+ }: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
24
+ return <MenubarPrimitive.Portal {...props} />
25
+ }
26
+
27
+ function MenubarRadioGroup({
28
+ ...props
29
+ }: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
30
+ return <MenubarPrimitive.RadioGroup {...props} />
31
+ }
32
+
33
+ function MenubarSub({
34
+ ...props
35
+ }: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
36
+ return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />
37
+ }
38
+
39
+ const Menubar = React.forwardRef<
40
+ React.ElementRef<typeof MenubarPrimitive.Root>,
41
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Root>
42
+ >(({ className, ...props }, ref) => (
43
+ <MenubarPrimitive.Root
44
+ ref={ref}
45
+ className={cn(
46
+ "flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm",
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ ))
52
+ Menubar.displayName = MenubarPrimitive.Root.displayName
53
+
54
+ const MenubarTrigger = React.forwardRef<
55
+ React.ElementRef<typeof MenubarPrimitive.Trigger>,
56
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Trigger>
57
+ >(({ className, ...props }, ref) => (
58
+ <MenubarPrimitive.Trigger
59
+ ref={ref}
60
+ className={cn(
61
+ "flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
62
+ className
63
+ )}
64
+ {...props}
65
+ />
66
+ ))
67
+ MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName
68
+
69
+ const MenubarSubTrigger = React.forwardRef<
70
+ React.ElementRef<typeof MenubarPrimitive.SubTrigger>,
71
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubTrigger> & {
72
+ inset?: boolean
73
+ }
74
+ >(({ className, inset, children, ...props }, ref) => (
75
+ <MenubarPrimitive.SubTrigger
76
+ ref={ref}
77
+ className={cn(
78
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
79
+ inset && "pl-8",
80
+ className
81
+ )}
82
+ {...props}
83
+ >
84
+ {children}
85
+ <ChevronRight className="ml-auto h-4 w-4" />
86
+ </MenubarPrimitive.SubTrigger>
87
+ ))
88
+ MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName
89
+
90
+ const MenubarSubContent = React.forwardRef<
91
+ React.ElementRef<typeof MenubarPrimitive.SubContent>,
92
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.SubContent>
93
+ >(({ className, ...props }, ref) => (
94
+ <MenubarPrimitive.SubContent
95
+ ref={ref}
96
+ className={cn(
97
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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 origin-[--radix-menubar-content-transform-origin]",
98
+ className
99
+ )}
100
+ {...props}
101
+ />
102
+ ))
103
+ MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName
104
+
105
+ const MenubarContent = React.forwardRef<
106
+ React.ElementRef<typeof MenubarPrimitive.Content>,
107
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Content>
108
+ >(
109
+ (
110
+ { className, align = "start", alignOffset = -4, sideOffset = 8, ...props },
111
+ ref
112
+ ) => (
113
+ <MenubarPrimitive.Portal>
114
+ <MenubarPrimitive.Content
115
+ ref={ref}
116
+ align={align}
117
+ alignOffset={alignOffset}
118
+ sideOffset={sideOffset}
119
+ className={cn(
120
+ "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in 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 origin-[--radix-menubar-content-transform-origin]",
121
+ className
122
+ )}
123
+ {...props}
124
+ />
125
+ </MenubarPrimitive.Portal>
126
+ )
127
+ )
128
+ MenubarContent.displayName = MenubarPrimitive.Content.displayName
129
+
130
+ const MenubarItem = React.forwardRef<
131
+ React.ElementRef<typeof MenubarPrimitive.Item>,
132
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Item> & {
133
+ inset?: boolean
134
+ }
135
+ >(({ className, inset, ...props }, ref) => (
136
+ <MenubarPrimitive.Item
137
+ ref={ref}
138
+ className={cn(
139
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
140
+ inset && "pl-8",
141
+ className
142
+ )}
143
+ {...props}
144
+ />
145
+ ))
146
+ MenubarItem.displayName = MenubarPrimitive.Item.displayName
147
+
148
+ const MenubarCheckboxItem = React.forwardRef<
149
+ React.ElementRef<typeof MenubarPrimitive.CheckboxItem>,
150
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.CheckboxItem>
151
+ >(({ className, children, checked, ...props }, ref) => (
152
+ <MenubarPrimitive.CheckboxItem
153
+ ref={ref}
154
+ className={cn(
155
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
156
+ className
157
+ )}
158
+ checked={checked}
159
+ {...props}
160
+ >
161
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
162
+ <MenubarPrimitive.ItemIndicator>
163
+ <Check className="h-4 w-4" />
164
+ </MenubarPrimitive.ItemIndicator>
165
+ </span>
166
+ {children}
167
+ </MenubarPrimitive.CheckboxItem>
168
+ ))
169
+ MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName
170
+
171
+ const MenubarRadioItem = React.forwardRef<
172
+ React.ElementRef<typeof MenubarPrimitive.RadioItem>,
173
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.RadioItem>
174
+ >(({ className, children, ...props }, ref) => (
175
+ <MenubarPrimitive.RadioItem
176
+ ref={ref}
177
+ className={cn(
178
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
179
+ className
180
+ )}
181
+ {...props}
182
+ >
183
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
184
+ <MenubarPrimitive.ItemIndicator>
185
+ <Circle className="h-4 w-4 fill-current" />
186
+ </MenubarPrimitive.ItemIndicator>
187
+ </span>
188
+ {children}
189
+ </MenubarPrimitive.RadioItem>
190
+ ))
191
+ MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName
192
+
193
+ const MenubarLabel = React.forwardRef<
194
+ React.ElementRef<typeof MenubarPrimitive.Label>,
195
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Label> & {
196
+ inset?: boolean
197
+ }
198
+ >(({ className, inset, ...props }, ref) => (
199
+ <MenubarPrimitive.Label
200
+ ref={ref}
201
+ className={cn(
202
+ "px-2 py-1.5 text-sm font-semibold",
203
+ inset && "pl-8",
204
+ className
205
+ )}
206
+ {...props}
207
+ />
208
+ ))
209
+ MenubarLabel.displayName = MenubarPrimitive.Label.displayName
210
+
211
+ const MenubarSeparator = React.forwardRef<
212
+ React.ElementRef<typeof MenubarPrimitive.Separator>,
213
+ React.ComponentPropsWithoutRef<typeof MenubarPrimitive.Separator>
214
+ >(({ className, ...props }, ref) => (
215
+ <MenubarPrimitive.Separator
216
+ ref={ref}
217
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
218
+ {...props}
219
+ />
220
+ ))
221
+ MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName
222
+
223
+ const MenubarShortcut = ({
224
+ className,
225
+ ...props
226
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
227
+ return (
228
+ <span
229
+ className={cn(
230
+ "ml-auto text-xs tracking-widest text-muted-foreground",
231
+ className
232
+ )}
233
+ {...props}
234
+ />
235
+ )
236
+ }
237
+ MenubarShortcut.displayname = "MenubarShortcut"
238
+
239
+ export {
240
+ Menubar,
241
+ MenubarMenu,
242
+ MenubarTrigger,
243
+ MenubarContent,
244
+ MenubarItem,
245
+ MenubarSeparator,
246
+ MenubarLabel,
247
+ MenubarCheckboxItem,
248
+ MenubarRadioGroup,
249
+ MenubarRadioItem,
250
+ MenubarPortal,
251
+ MenubarSubContent,
252
+ MenubarSubTrigger,
253
+ MenubarGroup,
254
+ MenubarSub,
255
+ MenubarShortcut,
256
+ }
src/components/ui/navigation-menu.tsx ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
3
+ import { cva } from "class-variance-authority"
4
+ import { ChevronDown } from "lucide-react"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const NavigationMenu = React.forwardRef<
9
+ React.ElementRef<typeof NavigationMenuPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
11
+ >(({ className, children, ...props }, ref) => (
12
+ <NavigationMenuPrimitive.Root
13
+ ref={ref}
14
+ className={cn(
15
+ "relative z-10 flex max-w-max flex-1 items-center justify-center",
16
+ className
17
+ )}
18
+ {...props}
19
+ >
20
+ {children}
21
+ <NavigationMenuViewport />
22
+ </NavigationMenuPrimitive.Root>
23
+ ))
24
+ NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
25
+
26
+ const NavigationMenuList = React.forwardRef<
27
+ React.ElementRef<typeof NavigationMenuPrimitive.List>,
28
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
29
+ >(({ className, ...props }, ref) => (
30
+ <NavigationMenuPrimitive.List
31
+ ref={ref}
32
+ className={cn(
33
+ "group flex flex-1 list-none items-center justify-center space-x-1",
34
+ className
35
+ )}
36
+ {...props}
37
+ />
38
+ ))
39
+ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
40
+
41
+ const NavigationMenuItem = NavigationMenuPrimitive.Item
42
+
43
+ const navigationMenuTriggerStyle = cva(
44
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:text-accent-foreground data-[state=open]:bg-accent/50 data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent"
45
+ )
46
+
47
+ const NavigationMenuTrigger = React.forwardRef<
48
+ React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
49
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
50
+ >(({ className, children, ...props }, ref) => (
51
+ <NavigationMenuPrimitive.Trigger
52
+ ref={ref}
53
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
54
+ {...props}
55
+ >
56
+ {children}{" "}
57
+ <ChevronDown
58
+ className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
59
+ aria-hidden="true"
60
+ />
61
+ </NavigationMenuPrimitive.Trigger>
62
+ ))
63
+ NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
64
+
65
+ const NavigationMenuContent = React.forwardRef<
66
+ React.ElementRef<typeof NavigationMenuPrimitive.Content>,
67
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
68
+ >(({ className, ...props }, ref) => (
69
+ <NavigationMenuPrimitive.Content
70
+ ref={ref}
71
+ className={cn(
72
+ "left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
73
+ className
74
+ )}
75
+ {...props}
76
+ />
77
+ ))
78
+ NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
79
+
80
+ const NavigationMenuLink = NavigationMenuPrimitive.Link
81
+
82
+ const NavigationMenuViewport = React.forwardRef<
83
+ React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
84
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
85
+ >(({ className, ...props }, ref) => (
86
+ <div className={cn("absolute left-0 top-full flex justify-center")}>
87
+ <NavigationMenuPrimitive.Viewport
88
+ className={cn(
89
+ "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
90
+ className
91
+ )}
92
+ ref={ref}
93
+ {...props}
94
+ />
95
+ </div>
96
+ ))
97
+ NavigationMenuViewport.displayName =
98
+ NavigationMenuPrimitive.Viewport.displayName
99
+
100
+ const NavigationMenuIndicator = React.forwardRef<
101
+ React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
102
+ React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
103
+ >(({ className, ...props }, ref) => (
104
+ <NavigationMenuPrimitive.Indicator
105
+ ref={ref}
106
+ className={cn(
107
+ "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
108
+ className
109
+ )}
110
+ {...props}
111
+ >
112
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
113
+ </NavigationMenuPrimitive.Indicator>
114
+ ))
115
+ NavigationMenuIndicator.displayName =
116
+ NavigationMenuPrimitive.Indicator.displayName
117
+
118
+ export {
119
+ navigationMenuTriggerStyle,
120
+ NavigationMenu,
121
+ NavigationMenuList,
122
+ NavigationMenuItem,
123
+ NavigationMenuContent,
124
+ NavigationMenuTrigger,
125
+ NavigationMenuLink,
126
+ NavigationMenuIndicator,
127
+ NavigationMenuViewport,
128
+ }
src/components/ui/popover.tsx ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Popover = PopoverPrimitive.Root
9
+
10
+ const PopoverTrigger = PopoverPrimitive.Trigger
11
+
12
+ const PopoverAnchor = PopoverPrimitive.Anchor
13
+
14
+ const PopoverContent = React.forwardRef<
15
+ React.ElementRef<typeof PopoverPrimitive.Content>,
16
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
17
+ >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
18
+ <PopoverPrimitive.Portal>
19
+ <PopoverPrimitive.Content
20
+ ref={ref}
21
+ align={align}
22
+ sideOffset={sideOffset}
23
+ className={cn(
24
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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 origin-[--radix-popover-content-transform-origin]",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ </PopoverPrimitive.Portal>
30
+ ))
31
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName
32
+
33
+ export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
src/components/ui/progress.tsx ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as ProgressPrimitive from "@radix-ui/react-progress"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const Progress = React.forwardRef<
7
+ React.ElementRef<typeof ProgressPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
9
+ >(({ className, value, ...props }, ref) => (
10
+ <ProgressPrimitive.Root
11
+ ref={ref}
12
+ className={cn(
13
+ "relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
14
+ className
15
+ )}
16
+ {...props}
17
+ >
18
+ <ProgressPrimitive.Indicator
19
+ className="h-full w-full flex-1 bg-primary transition-all"
20
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
21
+ />
22
+ </ProgressPrimitive.Root>
23
+ ))
24
+ Progress.displayName = ProgressPrimitive.Root.displayName
25
+
26
+ export { Progress }
src/components/ui/radio-group.tsx ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
3
+ import { Circle } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const RadioGroup = React.forwardRef<
8
+ React.ElementRef<typeof RadioGroupPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
10
+ >(({ className, ...props }, ref) => {
11
+ return (
12
+ <RadioGroupPrimitive.Root
13
+ className={cn("grid gap-2", className)}
14
+ {...props}
15
+ ref={ref}
16
+ />
17
+ )
18
+ })
19
+ RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
20
+
21
+ const RadioGroupItem = React.forwardRef<
22
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
23
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
24
+ >(({ className, ...props }, ref) => {
25
+ return (
26
+ <RadioGroupPrimitive.Item
27
+ ref={ref}
28
+ className={cn(
29
+ "aspect-square h-4 w-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
30
+ className
31
+ )}
32
+ {...props}
33
+ >
34
+ <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
35
+ <Circle className="h-3.5 w-3.5 fill-primary" />
36
+ </RadioGroupPrimitive.Indicator>
37
+ </RadioGroupPrimitive.Item>
38
+ )
39
+ })
40
+ RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
41
+
42
+ export { RadioGroup, RadioGroupItem }
src/components/ui/resizable.tsx ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { GripVertical } from "lucide-react"
2
+ import { Group, Panel, Separator } from "react-resizable-panels"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const ResizablePanelGroup = ({
7
+ className,
8
+ ...props
9
+ }: React.ComponentProps<typeof Group>) => (
10
+ <Group
11
+ className={cn(
12
+ "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ )
18
+
19
+ const ResizablePanel = Panel
20
+
21
+ const ResizableHandle = ({
22
+ withHandle,
23
+ className,
24
+ ...props
25
+ }: React.ComponentProps<typeof Separator> & {
26
+ withHandle?: boolean
27
+ }) => (
28
+ <Separator
29
+ className={cn(
30
+ "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
31
+ className
32
+ )}
33
+ {...props}
34
+ >
35
+ {withHandle && (
36
+ <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
37
+ <GripVertical className="h-2.5 w-2.5" />
38
+ </div>
39
+ )}
40
+ </Separator>
41
+ )
42
+
43
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
src/components/ui/scroll-area.tsx ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const ScrollArea = React.forwardRef<
7
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
9
+ >(({ className, children, ...props }, ref) => (
10
+ <ScrollAreaPrimitive.Root
11
+ ref={ref}
12
+ className={cn("relative overflow-hidden", className)}
13
+ {...props}
14
+ >
15
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
16
+ {children}
17
+ </ScrollAreaPrimitive.Viewport>
18
+ <ScrollBar />
19
+ <ScrollAreaPrimitive.Corner />
20
+ </ScrollAreaPrimitive.Root>
21
+ ))
22
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
23
+
24
+ const ScrollBar = React.forwardRef<
25
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
26
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
27
+ >(({ className, orientation = "vertical", ...props }, ref) => (
28
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
29
+ ref={ref}
30
+ orientation={orientation}
31
+ className={cn(
32
+ "flex touch-none select-none transition-colors",
33
+ orientation === "vertical" &&
34
+ "h-full w-2.5 border-l border-l-transparent p-[1px]",
35
+ orientation === "horizontal" &&
36
+ "h-2.5 flex-col border-t border-t-transparent p-[1px]",
37
+ className
38
+ )}
39
+ {...props}
40
+ >
41
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
42
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
43
+ ))
44
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
45
+
46
+ export { ScrollArea, ScrollBar }
src/components/ui/select.tsx ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as SelectPrimitive from "@radix-ui/react-select"
3
+ import { Check, ChevronDown, ChevronUp } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const Select = SelectPrimitive.Root
8
+
9
+ const SelectGroup = SelectPrimitive.Group
10
+
11
+ const SelectValue = SelectPrimitive.Value
12
+
13
+ const SelectTrigger = React.forwardRef<
14
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
15
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
16
+ >(({ className, children, ...props }, ref) => (
17
+ <SelectPrimitive.Trigger
18
+ ref={ref}
19
+ className={cn(
20
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
21
+ className
22
+ )}
23
+ {...props}
24
+ >
25
+ {children}
26
+ <SelectPrimitive.Icon asChild>
27
+ <ChevronDown className="h-4 w-4 opacity-50" />
28
+ </SelectPrimitive.Icon>
29
+ </SelectPrimitive.Trigger>
30
+ ))
31
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
32
+
33
+ const SelectScrollUpButton = React.forwardRef<
34
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
35
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
36
+ >(({ className, ...props }, ref) => (
37
+ <SelectPrimitive.ScrollUpButton
38
+ ref={ref}
39
+ className={cn(
40
+ "flex cursor-default items-center justify-center py-1",
41
+ className
42
+ )}
43
+ {...props}
44
+ >
45
+ <ChevronUp className="h-4 w-4" />
46
+ </SelectPrimitive.ScrollUpButton>
47
+ ))
48
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
49
+
50
+ const SelectScrollDownButton = React.forwardRef<
51
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
52
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
53
+ >(({ className, ...props }, ref) => (
54
+ <SelectPrimitive.ScrollDownButton
55
+ ref={ref}
56
+ className={cn(
57
+ "flex cursor-default items-center justify-center py-1",
58
+ className
59
+ )}
60
+ {...props}
61
+ >
62
+ <ChevronDown className="h-4 w-4" />
63
+ </SelectPrimitive.ScrollDownButton>
64
+ ))
65
+ SelectScrollDownButton.displayName =
66
+ SelectPrimitive.ScrollDownButton.displayName
67
+
68
+ const SelectContent = React.forwardRef<
69
+ React.ElementRef<typeof SelectPrimitive.Content>,
70
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
71
+ >(({ className, children, position = "popper", ...props }, ref) => (
72
+ <SelectPrimitive.Portal>
73
+ <SelectPrimitive.Content
74
+ ref={ref}
75
+ className={cn(
76
+ "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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 origin-[--radix-select-content-transform-origin]",
77
+ position === "popper" &&
78
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
79
+ className
80
+ )}
81
+ position={position}
82
+ {...props}
83
+ >
84
+ <SelectScrollUpButton />
85
+ <SelectPrimitive.Viewport
86
+ className={cn(
87
+ "p-1",
88
+ position === "popper" &&
89
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
90
+ )}
91
+ >
92
+ {children}
93
+ </SelectPrimitive.Viewport>
94
+ <SelectScrollDownButton />
95
+ </SelectPrimitive.Content>
96
+ </SelectPrimitive.Portal>
97
+ ))
98
+ SelectContent.displayName = SelectPrimitive.Content.displayName
99
+
100
+ const SelectLabel = React.forwardRef<
101
+ React.ElementRef<typeof SelectPrimitive.Label>,
102
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
103
+ >(({ className, ...props }, ref) => (
104
+ <SelectPrimitive.Label
105
+ ref={ref}
106
+ className={cn("px-2 py-1.5 text-sm font-semibold", className)}
107
+ {...props}
108
+ />
109
+ ))
110
+ SelectLabel.displayName = SelectPrimitive.Label.displayName
111
+
112
+ const SelectItem = React.forwardRef<
113
+ React.ElementRef<typeof SelectPrimitive.Item>,
114
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
115
+ >(({ className, children, ...props }, ref) => (
116
+ <SelectPrimitive.Item
117
+ ref={ref}
118
+ className={cn(
119
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
120
+ className
121
+ )}
122
+ {...props}
123
+ >
124
+ <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
125
+ <SelectPrimitive.ItemIndicator>
126
+ <Check className="h-4 w-4" />
127
+ </SelectPrimitive.ItemIndicator>
128
+ </span>
129
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
130
+ </SelectPrimitive.Item>
131
+ ))
132
+ SelectItem.displayName = SelectPrimitive.Item.displayName
133
+
134
+ const SelectSeparator = React.forwardRef<
135
+ React.ElementRef<typeof SelectPrimitive.Separator>,
136
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
137
+ >(({ className, ...props }, ref) => (
138
+ <SelectPrimitive.Separator
139
+ ref={ref}
140
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
141
+ {...props}
142
+ />
143
+ ))
144
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName
145
+
146
+ export {
147
+ Select,
148
+ SelectGroup,
149
+ SelectValue,
150
+ SelectTrigger,
151
+ SelectContent,
152
+ SelectLabel,
153
+ SelectItem,
154
+ SelectSeparator,
155
+ SelectScrollUpButton,
156
+ SelectScrollDownButton,
157
+ }
src/components/ui/separator.tsx ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SeparatorPrimitive from "@radix-ui/react-separator"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Separator = React.forwardRef<
9
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
11
+ >(
12
+ (
13
+ { className, orientation = "horizontal", decorative = true, ...props },
14
+ ref
15
+ ) => (
16
+ <SeparatorPrimitive.Root
17
+ ref={ref}
18
+ decorative={decorative}
19
+ orientation={orientation}
20
+ className={cn(
21
+ "shrink-0 bg-border",
22
+ orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ )
28
+ )
29
+ Separator.displayName = SeparatorPrimitive.Root.displayName
30
+
31
+ export { Separator }
src/components/ui/sheet.tsx ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SheetPrimitive from "@radix-ui/react-dialog"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import { X } from "lucide-react"
7
+
8
+ import { cn } from "@/lib/utils"
9
+
10
+ const Sheet = SheetPrimitive.Root
11
+
12
+ const SheetTrigger = SheetPrimitive.Trigger
13
+
14
+ const SheetClose = SheetPrimitive.Close
15
+
16
+ const SheetPortal = SheetPrimitive.Portal
17
+
18
+ const SheetOverlay = React.forwardRef<
19
+ React.ElementRef<typeof SheetPrimitive.Overlay>,
20
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
21
+ >(({ className, ...props }, ref) => (
22
+ <SheetPrimitive.Overlay
23
+ className={cn(
24
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
25
+ className
26
+ )}
27
+ {...props}
28
+ ref={ref}
29
+ />
30
+ ))
31
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32
+
33
+ const sheetVariants = cva(
34
+ "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
35
+ {
36
+ variants: {
37
+ side: {
38
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
39
+ bottom:
40
+ "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
41
+ left: "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",
42
+ right:
43
+ "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",
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ side: "right",
48
+ },
49
+ }
50
+ )
51
+
52
+ interface SheetContentProps
53
+ extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
54
+ VariantProps<typeof sheetVariants> {}
55
+
56
+ const SheetContent = React.forwardRef<
57
+ React.ElementRef<typeof SheetPrimitive.Content>,
58
+ SheetContentProps
59
+ >(({ side = "right", className, children, ...props }, ref) => (
60
+ <SheetPortal>
61
+ <SheetOverlay />
62
+ <SheetPrimitive.Content
63
+ ref={ref}
64
+ className={cn(sheetVariants({ side }), className)}
65
+ {...props}
66
+ >
67
+ <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
68
+ <X className="h-4 w-4" />
69
+ <span className="sr-only">Close</span>
70
+ </SheetPrimitive.Close>
71
+ {children}
72
+ </SheetPrimitive.Content>
73
+ </SheetPortal>
74
+ ))
75
+ SheetContent.displayName = SheetPrimitive.Content.displayName
76
+
77
+ const SheetHeader = ({
78
+ className,
79
+ ...props
80
+ }: React.HTMLAttributes<HTMLDivElement>) => (
81
+ <div
82
+ className={cn(
83
+ "flex flex-col space-y-2 text-center sm:text-left",
84
+ className
85
+ )}
86
+ {...props}
87
+ />
88
+ )
89
+ SheetHeader.displayName = "SheetHeader"
90
+
91
+ const SheetFooter = ({
92
+ className,
93
+ ...props
94
+ }: React.HTMLAttributes<HTMLDivElement>) => (
95
+ <div
96
+ className={cn(
97
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
98
+ className
99
+ )}
100
+ {...props}
101
+ />
102
+ )
103
+ SheetFooter.displayName = "SheetFooter"
104
+
105
+ const SheetTitle = React.forwardRef<
106
+ React.ElementRef<typeof SheetPrimitive.Title>,
107
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
108
+ >(({ className, ...props }, ref) => (
109
+ <SheetPrimitive.Title
110
+ ref={ref}
111
+ className={cn("text-lg font-semibold text-foreground", className)}
112
+ {...props}
113
+ />
114
+ ))
115
+ SheetTitle.displayName = SheetPrimitive.Title.displayName
116
+
117
+ const SheetDescription = React.forwardRef<
118
+ React.ElementRef<typeof SheetPrimitive.Description>,
119
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
120
+ >(({ className, ...props }, ref) => (
121
+ <SheetPrimitive.Description
122
+ ref={ref}
123
+ className={cn("text-sm text-muted-foreground", className)}
124
+ {...props}
125
+ />
126
+ ))
127
+ SheetDescription.displayName = SheetPrimitive.Description.displayName
128
+
129
+ export {
130
+ Sheet,
131
+ SheetPortal,
132
+ SheetOverlay,
133
+ SheetTrigger,
134
+ SheetClose,
135
+ SheetContent,
136
+ SheetHeader,
137
+ SheetFooter,
138
+ SheetTitle,
139
+ SheetDescription,
140
+ }
src/components/ui/skeleton.tsx ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { cn } from "@/lib/utils"
2
+
3
+ function Skeleton({
4
+ className,
5
+ ...props
6
+ }: React.HTMLAttributes<HTMLDivElement>) {
7
+ return (
8
+ <div
9
+ className={cn("animate-pulse rounded-md bg-[var(--bg-subtle)]", className)}
10
+ {...props}
11
+ />
12
+ )
13
+ }
14
+
15
+ export { Skeleton }
src/components/ui/slider.tsx ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as SliderPrimitive from "@radix-ui/react-slider"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const Slider = React.forwardRef<
7
+ React.ElementRef<typeof SliderPrimitive.Root>,
8
+ React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
9
+ >(({ className, ...props }, ref) => (
10
+ <SliderPrimitive.Root
11
+ ref={ref}
12
+ className={cn(
13
+ "relative flex w-full touch-none select-none items-center",
14
+ className
15
+ )}
16
+ {...props}
17
+ >
18
+ <SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
19
+ <SliderPrimitive.Range className="absolute h-full bg-primary" />
20
+ </SliderPrimitive.Track>
21
+ <SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
22
+ </SliderPrimitive.Root>
23
+ ))
24
+ Slider.displayName = SliderPrimitive.Root.displayName
25
+
26
+ export { Slider }
src/components/ui/sonner.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useTheme } from "next-themes"
2
+ import { Toaster as Sonner } from "sonner"
3
+
4
+ type ToasterProps = React.ComponentProps<typeof Sonner>
5
+
6
+ const Toaster = ({ ...props }: ToasterProps) => {
7
+ const { theme = "system" } = useTheme()
8
+
9
+ return (
10
+ <Sonner
11
+ theme={theme as ToasterProps["theme"]}
12
+ className="toaster group"
13
+ toastOptions={{
14
+ classNames: {
15
+ toast:
16
+ "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
17
+ description: "group-[.toast]:text-muted-foreground",
18
+ actionButton:
19
+ "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
20
+ cancelButton:
21
+ "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
22
+ },
23
+ }}
24
+ {...props}
25
+ />
26
+ )
27
+ }
28
+
29
+ export { Toaster }
src/components/ui/switch.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SwitchPrimitives from "@radix-ui/react-switch"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ const Switch = React.forwardRef<
9
+ React.ElementRef<typeof SwitchPrimitives.Root>,
10
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
11
+ >(({ className, ...props }, ref) => (
12
+ <SwitchPrimitives.Root
13
+ className={cn(
14
+ "peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
15
+ className
16
+ )}
17
+ {...props}
18
+ ref={ref}
19
+ >
20
+ <SwitchPrimitives.Thumb
21
+ className={cn(
22
+ "pointer-events-none block h-4 w-4 rounded-full bg-[var(--neutral-0)] shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
23
+ )}
24
+ />
25
+ </SwitchPrimitives.Root>
26
+ ))
27
+ Switch.displayName = SwitchPrimitives.Root.displayName
28
+
29
+ export { Switch }
src/components/ui/table.tsx ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Table = React.forwardRef<
6
+ HTMLTableElement,
7
+ React.HTMLAttributes<HTMLTableElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div className="relative w-full overflow-auto">
10
+ <table
11
+ ref={ref}
12
+ className={cn("w-full caption-bottom text-sm", className)}
13
+ {...props}
14
+ />
15
+ </div>
16
+ ))
17
+ Table.displayName = "Table"
18
+
19
+ const TableHeader = React.forwardRef<
20
+ HTMLTableSectionElement,
21
+ React.HTMLAttributes<HTMLTableSectionElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
24
+ ))
25
+ TableHeader.displayName = "TableHeader"
26
+
27
+ const TableBody = React.forwardRef<
28
+ HTMLTableSectionElement,
29
+ React.HTMLAttributes<HTMLTableSectionElement>
30
+ >(({ className, ...props }, ref) => (
31
+ <tbody
32
+ ref={ref}
33
+ className={cn("[&_tr:last-child]:border-0", className)}
34
+ {...props}
35
+ />
36
+ ))
37
+ TableBody.displayName = "TableBody"
38
+
39
+ const TableFooter = React.forwardRef<
40
+ HTMLTableSectionElement,
41
+ React.HTMLAttributes<HTMLTableSectionElement>
42
+ >(({ className, ...props }, ref) => (
43
+ <tfoot
44
+ ref={ref}
45
+ className={cn(
46
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ ))
52
+ TableFooter.displayName = "TableFooter"
53
+
54
+ const TableRow = React.forwardRef<
55
+ HTMLTableRowElement,
56
+ React.HTMLAttributes<HTMLTableRowElement>
57
+ >(({ className, ...props }, ref) => (
58
+ <tr
59
+ ref={ref}
60
+ className={cn(
61
+ "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
62
+ className
63
+ )}
64
+ {...props}
65
+ />
66
+ ))
67
+ TableRow.displayName = "TableRow"
68
+
69
+ const TableHead = React.forwardRef<
70
+ HTMLTableCellElement,
71
+ React.ThHTMLAttributes<HTMLTableCellElement>
72
+ >(({ className, ...props }, ref) => (
73
+ <th
74
+ ref={ref}
75
+ className={cn(
76
+ "h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ ))
82
+ TableHead.displayName = "TableHead"
83
+
84
+ const TableCell = React.forwardRef<
85
+ HTMLTableCellElement,
86
+ React.TdHTMLAttributes<HTMLTableCellElement>
87
+ >(({ className, ...props }, ref) => (
88
+ <td
89
+ ref={ref}
90
+ className={cn(
91
+ "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
92
+ className
93
+ )}
94
+ {...props}
95
+ />
96
+ ))
97
+ TableCell.displayName = "TableCell"
98
+
99
+ const TableCaption = React.forwardRef<
100
+ HTMLTableCaptionElement,
101
+ React.HTMLAttributes<HTMLTableCaptionElement>
102
+ >(({ className, ...props }, ref) => (
103
+ <caption
104
+ ref={ref}
105
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
106
+ {...props}
107
+ />
108
+ ))
109
+ TableCaption.displayName = "TableCaption"
110
+
111
+ export {
112
+ Table,
113
+ TableHeader,
114
+ TableBody,
115
+ TableFooter,
116
+ TableHead,
117
+ TableRow,
118
+ TableCell,
119
+ TableCaption,
120
+ }
src/components/ui/tabs.tsx ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as TabsPrimitive from "@radix-ui/react-tabs"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const Tabs = TabsPrimitive.Root
7
+
8
+ const TabsList = React.forwardRef<
9
+ React.ElementRef<typeof TabsPrimitive.List>,
10
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
11
+ >(({ className, ...props }, ref) => (
12
+ <TabsPrimitive.List
13
+ ref={ref}
14
+ className={cn(
15
+ "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ ))
21
+ TabsList.displayName = TabsPrimitive.List.displayName
22
+
23
+ const TabsTrigger = React.forwardRef<
24
+ React.ElementRef<typeof TabsPrimitive.Trigger>,
25
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
26
+ >(({ className, ...props }, ref) => (
27
+ <TabsPrimitive.Trigger
28
+ ref={ref}
29
+ className={cn(
30
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
31
+ className
32
+ )}
33
+ {...props}
34
+ />
35
+ ))
36
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
37
+
38
+ const TabsContent = React.forwardRef<
39
+ React.ElementRef<typeof TabsPrimitive.Content>,
40
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
41
+ >(({ className, ...props }, ref) => (
42
+ <TabsPrimitive.Content
43
+ ref={ref}
44
+ className={cn(
45
+ "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ ))
51
+ TabsContent.displayName = TabsPrimitive.Content.displayName
52
+
53
+ export { Tabs, TabsList, TabsTrigger, TabsContent }
src/components/ui/textarea.tsx ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Textarea = React.forwardRef<
6
+ HTMLTextAreaElement,
7
+ React.ComponentProps<"textarea">
8
+ >(({ className, ...props }, ref) => {
9
+ return (
10
+ <textarea
11
+ className={cn(
12
+ "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
13
+ className
14
+ )}
15
+ ref={ref}
16
+ {...props}
17
+ />
18
+ )
19
+ })
20
+ Textarea.displayName = "Textarea"
21
+
22
+ export { Textarea }
src/components/ui/toast.tsx ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as ToastPrimitives from "@radix-ui/react-toast"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import { X } from "lucide-react"
7
+
8
+ import { cn } from "@/lib/utils"
9
+
10
+ const ToastProvider = ToastPrimitives.Provider
11
+
12
+ const ToastViewport = React.forwardRef<
13
+ React.ElementRef<typeof ToastPrimitives.Viewport>,
14
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
15
+ >(({ className, ...props }, ref) => (
16
+ <ToastPrimitives.Viewport
17
+ ref={ref}
18
+ className={cn(
19
+ "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
20
+ className
21
+ )}
22
+ {...props}
23
+ />
24
+ ))
25
+ ToastViewport.displayName = ToastPrimitives.Viewport.displayName
26
+
27
+ const toastVariants = cva(
28
+ "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
29
+ {
30
+ variants: {
31
+ variant: {
32
+ default: "border bg-background text-foreground",
33
+ destructive:
34
+ "destructive group border-destructive bg-destructive text-destructive-foreground",
35
+ },
36
+ },
37
+ defaultVariants: {
38
+ variant: "default",
39
+ },
40
+ }
41
+ )
42
+
43
+ const Toast = React.forwardRef<
44
+ React.ElementRef<typeof ToastPrimitives.Root>,
45
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
46
+ VariantProps<typeof toastVariants>
47
+ >(({ className, variant, ...props }, ref) => {
48
+ return (
49
+ <ToastPrimitives.Root
50
+ ref={ref}
51
+ className={cn(toastVariants({ variant }), className)}
52
+ {...props}
53
+ />
54
+ )
55
+ })
56
+ Toast.displayName = ToastPrimitives.Root.displayName
57
+
58
+ const ToastAction = React.forwardRef<
59
+ React.ElementRef<typeof ToastPrimitives.Action>,
60
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
61
+ >(({ className, ...props }, ref) => (
62
+ <ToastPrimitives.Action
63
+ ref={ref}
64
+ className={cn(
65
+ "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
66
+ className
67
+ )}
68
+ {...props}
69
+ />
70
+ ))
71
+ ToastAction.displayName = ToastPrimitives.Action.displayName
72
+
73
+ const ToastClose = React.forwardRef<
74
+ React.ElementRef<typeof ToastPrimitives.Close>,
75
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
76
+ >(({ className, ...props }, ref) => (
77
+ <ToastPrimitives.Close
78
+ ref={ref}
79
+ className={cn(
80
+ "absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
81
+ className
82
+ )}
83
+ toast-close=""
84
+ {...props}
85
+ >
86
+ <X className="h-4 w-4" />
87
+ </ToastPrimitives.Close>
88
+ ))
89
+ ToastClose.displayName = ToastPrimitives.Close.displayName
90
+
91
+ const ToastTitle = React.forwardRef<
92
+ React.ElementRef<typeof ToastPrimitives.Title>,
93
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
94
+ >(({ className, ...props }, ref) => (
95
+ <ToastPrimitives.Title
96
+ ref={ref}
97
+ className={cn("text-sm font-semibold [&+div]:text-xs", className)}
98
+ {...props}
99
+ />
100
+ ))
101
+ ToastTitle.displayName = ToastPrimitives.Title.displayName
102
+
103
+ const ToastDescription = React.forwardRef<
104
+ React.ElementRef<typeof ToastPrimitives.Description>,
105
+ React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
106
+ >(({ className, ...props }, ref) => (
107
+ <ToastPrimitives.Description
108
+ ref={ref}
109
+ className={cn("text-sm opacity-90", className)}
110
+ {...props}
111
+ />
112
+ ))
113
+ ToastDescription.displayName = ToastPrimitives.Description.displayName
114
+
115
+ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
116
+
117
+ type ToastActionElement = React.ReactElement<typeof ToastAction>
118
+
119
+ export {
120
+ type ToastProps,
121
+ type ToastActionElement,
122
+ ToastProvider,
123
+ ToastViewport,
124
+ Toast,
125
+ ToastTitle,
126
+ ToastDescription,
127
+ ToastClose,
128
+ ToastAction,
129
+ }
src/components/ui/toaster.tsx ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import { useToast } from "@/hooks/use-toast"
4
+ import {
5
+ Toast,
6
+ ToastClose,
7
+ ToastDescription,
8
+ ToastProvider,
9
+ ToastTitle,
10
+ ToastViewport,
11
+ } from "@/components/ui/toast"
12
+
13
+ export function Toaster() {
14
+ const { toasts } = useToast()
15
+
16
+ return (
17
+ <ToastProvider>
18
+ {toasts.map(function ({ id, title, description, action, ...props }) {
19
+ return (
20
+ <Toast key={id} {...props}>
21
+ <div className="grid gap-1">
22
+ {title && <ToastTitle>{title}</ToastTitle>}
23
+ {description && (
24
+ <ToastDescription>{description}</ToastDescription>
25
+ )}
26
+ </div>
27
+ {action}
28
+ <ToastClose />
29
+ </Toast>
30
+ )
31
+ })}
32
+ <ToastViewport />
33
+ </ToastProvider>
34
+ )
35
+ }
src/components/ui/toggle-group.tsx ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
3
+ import { type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+ import { toggleVariants } from "@/components/ui/toggle"
7
+
8
+ const ToggleGroupContext = React.createContext<
9
+ VariantProps<typeof toggleVariants>
10
+ >({
11
+ size: "default",
12
+ variant: "default",
13
+ })
14
+
15
+ const ToggleGroup = React.forwardRef<
16
+ React.ElementRef<typeof ToggleGroupPrimitive.Root>,
17
+ React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
18
+ VariantProps<typeof toggleVariants>
19
+ >(({ className, variant, size, children, ...props }, ref) => (
20
+ <ToggleGroupPrimitive.Root
21
+ ref={ref}
22
+ className={cn("flex items-center justify-center gap-1", className)}
23
+ {...props}
24
+ >
25
+ <ToggleGroupContext.Provider value={{ variant, size }}>
26
+ {children}
27
+ </ToggleGroupContext.Provider>
28
+ </ToggleGroupPrimitive.Root>
29
+ ))
30
+
31
+ ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
32
+
33
+ const ToggleGroupItem = React.forwardRef<
34
+ React.ElementRef<typeof ToggleGroupPrimitive.Item>,
35
+ React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
36
+ VariantProps<typeof toggleVariants>
37
+ >(({ className, children, variant, size, ...props }, ref) => {
38
+ const context = React.useContext(ToggleGroupContext)
39
+
40
+ return (
41
+ <ToggleGroupPrimitive.Item
42
+ ref={ref}
43
+ className={cn(
44
+ toggleVariants({
45
+ variant: context.variant || variant,
46
+ size: context.size || size,
47
+ }),
48
+ className
49
+ )}
50
+ {...props}
51
+ >
52
+ {children}
53
+ </ToggleGroupPrimitive.Item>
54
+ )
55
+ })
56
+
57
+ ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
58
+
59
+ export { ToggleGroup, ToggleGroupItem }
src/components/ui/toggle.tsx ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as TogglePrimitive from "@radix-ui/react-toggle"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const toggleVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-transparent",
13
+ outline:
14
+ "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
15
+ },
16
+ size: {
17
+ default: "h-9 px-2 min-w-9",
18
+ sm: "h-8 px-1.5 min-w-8",
19
+ lg: "h-10 px-2.5 min-w-10",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ size: "default",
25
+ },
26
+ }
27
+ )
28
+
29
+ const Toggle = React.forwardRef<
30
+ React.ElementRef<typeof TogglePrimitive.Root>,
31
+ React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
32
+ VariantProps<typeof toggleVariants>
33
+ >(({ className, variant, size, ...props }, ref) => (
34
+ <TogglePrimitive.Root
35
+ ref={ref}
36
+ className={cn(toggleVariants({ variant, size, className }))}
37
+ {...props}
38
+ />
39
+ ))
40
+
41
+ Toggle.displayName = TogglePrimitive.Root.displayName
42
+
43
+ export { Toggle, toggleVariants }
src/components/ui/tooltip.tsx ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const TooltipProvider = TooltipPrimitive.Provider
7
+
8
+ const Tooltip = TooltipPrimitive.Root
9
+
10
+ const TooltipTrigger = TooltipPrimitive.Trigger
11
+
12
+ const TooltipContent = React.forwardRef<
13
+ React.ElementRef<typeof TooltipPrimitive.Content>,
14
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
15
+ >(({ className, sideOffset = 4, ...props }, ref) => (
16
+ <TooltipPrimitive.Portal>
17
+ <TooltipPrimitive.Content
18
+ ref={ref}
19
+ sideOffset={sideOffset}
20
+ className={cn(
21
+ "z-50 w-fit text-balance rounded-[10px] border border-[var(--border-base)] bg-foreground/5 px-3 py-2 text-xs text-[var(--fg-subtle)] backdrop-blur-2xl animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 origin-[--radix-tooltip-content-transform-origin]",
22
+ className
23
+ )}
24
+ {...props}
25
+ />
26
+ </TooltipPrimitive.Portal>
27
+ ))
28
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName
29
+
30
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
src/entry-client.tsx ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import './index.css'
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // Cold-start guard: verify React is real BEFORE rendering.
5
+ //
6
+ // During Vite dep-optimization (cold start or after `npm install <pkg>`),
7
+ // Vite may serve placeholder "stub" modules where all React exports are
8
+ // undefined. Rendering with stub hooks causes "Cannot read properties of
9
+ // null (reading 'useState')".
10
+ //
11
+ // Strategy:
12
+ // 1. Dynamic-import React and check if useState is a function.
13
+ // 2. If stub → show a loading banner, schedule a reload with increasing
14
+ // delay, and do NOT mount any React tree (avoids the error entirely).
15
+ // 3. If real → dynamic-import App/ErrorBoundary and render normally.
16
+ // 4. Use createElement (not JSX) in this file so Vite doesn't inject a
17
+ // static `import { jsx } from 'react/jsx-dev-runtime'` which would
18
+ // itself be a stub and crash before our guard runs.
19
+ // ---------------------------------------------------------------------------
20
+
21
+ const RELOAD_KEY = '__dep_reload'
22
+ const MAX_RELOADS = 6
23
+ const RELOAD_WINDOW = 60_000 // 1 minute
24
+
25
+ function getReloads(): { c: number; t: number } {
26
+ try {
27
+ return JSON.parse(sessionStorage.getItem(RELOAD_KEY) || '{}') as { c: number; t: number }
28
+ } catch { return { c: 0, t: 0 } }
29
+ }
30
+
31
+ async function boot() {
32
+ const root = document.getElementById('root')!
33
+
34
+ try {
35
+ const React = await import('react')
36
+
37
+ // If useState is not a function, React is a dep-optimization stub
38
+ if (typeof React.useState !== 'function') throw new Error('dep-stub')
39
+
40
+ const { createElement } = React
41
+ const { createRoot, hydrateRoot } = await import('react-dom/client')
42
+ const { QueryClient, QueryClientProvider } = await import('@tanstack/react-query')
43
+ const { default: App } = await import('./App')
44
+ const { default: ErrorBoundary } = await import('./ErrorBoundary')
45
+
46
+ const queryClient = new QueryClient({
47
+ defaultOptions: { queries: {
48
+ refetchOnWindowFocus: false,
49
+ retry: 3,
50
+ retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 10000),
51
+ staleTime: 30_000,
52
+ } },
53
+ })
54
+
55
+ const children = createElement(ErrorBoundary, null,
56
+ createElement(QueryClientProvider, { client: queryClient },
57
+ createElement(App)
58
+ )
59
+ )
60
+
61
+ // Use hydrateRoot only when SSR rendered real app content. The scaffold's
62
+ // server entry intentionally returns a lightweight placeholder shell so
63
+ // SSR-incompatible libraries (for example echarts-for-react) cannot crash
64
+ // deploy-time render. Placeholder markup must be client-rendered, not hydrated.
65
+ const hasPlaceholder = !!root.querySelector('[data-surf-placeholder]')
66
+ if (root.childNodes.length > 0 && root.innerHTML !== '<!--ssr-outlet-->' && !hasPlaceholder) {
67
+ hydrateRoot(root, children)
68
+ } else {
69
+ root.innerHTML = ''
70
+ createRoot(root).render(children)
71
+ }
72
+
73
+ // React rendered successfully — signal to index.html fallback & reset counter
74
+ ;(window as any).__reactOk = true
75
+ sessionStorage.removeItem(RELOAD_KEY)
76
+
77
+ // Notify parent frame when real app content renders (not the placeholder).
78
+ // DO NOT REMOVE — the hosting app uses this to dismiss the loading overlay.
79
+ function notifyParentReady() {
80
+ if (!document.querySelector('[data-surf-placeholder]')) {
81
+ try { window.parent.postMessage({ type: 'surf-app-ready' }, '*') } catch { /* cross-origin — ignore */ }
82
+ }
83
+ }
84
+ notifyParentReady()
85
+ new MutationObserver(notifyParentReady).observe(root, { childList: true, subtree: true })
86
+ } catch {
87
+ // React is not ready — show loading banner and schedule reload
88
+ const prev = getReloads()
89
+ const count = (Date.now() - prev.t > RELOAD_WINDOW) ? 0 : prev.c
90
+
91
+ if (count < MAX_RELOADS) {
92
+ root.innerHTML = [
93
+ '<div style="padding:24px;text-align:center;font-family:system-ui,sans-serif">',
94
+ '<p style="color:#3b82f6;font-weight:600;margin:0 0 4px">Loading dependencies...</p>',
95
+ '<p style="color:#3b82f6;opacity:0.7;font-size:12px;margin:0">Reloading automatically</p>',
96
+ '</div>',
97
+ ].join('')
98
+ sessionStorage.setItem(RELOAD_KEY, JSON.stringify({ c: count + 1, t: Date.now() }))
99
+ // Increasing delay: 3s, 4s, 5s, ... gives Vite more time to finish
100
+ setTimeout(() => location.reload(), 3000 + count * 1000)
101
+ } else {
102
+ root.innerHTML = [
103
+ '<div style="padding:24px;text-align:center;font-family:system-ui,sans-serif">',
104
+ '<p style="color:#c0392b;font-weight:600;margin:0 0 4px">Failed to load dependencies</p>',
105
+ '<p style="color:#c0392b;opacity:0.8;font-size:12px;margin:0">Please refresh the page</p>',
106
+ '</div>',
107
+ ].join('')
108
+ }
109
+ }
110
+ }
111
+
112
+ boot()