jettypod 4.4.118 → 4.4.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/.env +4 -3
  2. package/Cargo.lock +6450 -0
  3. package/Cargo.toml +35 -0
  4. package/README.md +5 -1
  5. package/TAURI-MIGRATION-PLAN.md +840 -0
  6. package/apps/dashboard/app/connect-claude/page.tsx +5 -6
  7. package/apps/dashboard/app/decision/[id]/page.tsx +63 -58
  8. package/apps/dashboard/app/demo/gates/page.tsx +43 -45
  9. package/apps/dashboard/app/design-system/page.tsx +868 -0
  10. package/apps/dashboard/app/globals.css +80 -4
  11. package/apps/dashboard/app/install-claude/page.tsx +4 -6
  12. package/apps/dashboard/app/login/page.tsx +72 -54
  13. package/apps/dashboard/app/page.tsx +101 -48
  14. package/apps/dashboard/app/settings/page.tsx +61 -13
  15. package/apps/dashboard/app/signup/page.tsx +242 -0
  16. package/apps/dashboard/app/subscribe/page.tsx +0 -2
  17. package/apps/dashboard/app/tests/page.tsx +37 -4
  18. package/apps/dashboard/app/welcome/page.tsx +13 -16
  19. package/apps/dashboard/app/work/[id]/page.tsx +117 -118
  20. package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
  21. package/apps/dashboard/components/AppShell.tsx +92 -85
  22. package/apps/dashboard/components/CardMenu.tsx +45 -12
  23. package/apps/dashboard/components/ClaudePanel.tsx +771 -850
  24. package/apps/dashboard/components/ClaudePanelInput.tsx +43 -15
  25. package/apps/dashboard/components/ConnectClaudeScreen.tsx +17 -34
  26. package/apps/dashboard/components/CopyableId.tsx +3 -4
  27. package/apps/dashboard/components/DetailReviewActions.tsx +100 -0
  28. package/apps/dashboard/components/DragContext.tsx +134 -63
  29. package/apps/dashboard/components/DraggableCard.tsx +3 -5
  30. package/apps/dashboard/components/DropZone.tsx +6 -7
  31. package/apps/dashboard/components/EditableDetailDescription.tsx +7 -13
  32. package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
  33. package/apps/dashboard/components/EditableTitle.tsx +26 -7
  34. package/apps/dashboard/components/ElapsedTimer.tsx +66 -0
  35. package/apps/dashboard/components/EpicGroup.tsx +359 -0
  36. package/apps/dashboard/components/GateCard.tsx +79 -17
  37. package/apps/dashboard/components/GateChoiceCard.tsx +15 -18
  38. package/apps/dashboard/components/InstallClaudeScreen.tsx +15 -32
  39. package/apps/dashboard/components/JettyLoader.tsx +37 -0
  40. package/apps/dashboard/components/KanbanBoard.tsx +368 -958
  41. package/apps/dashboard/components/KanbanCard.tsx +740 -0
  42. package/apps/dashboard/components/LazyCard.tsx +62 -0
  43. package/apps/dashboard/components/LazyMarkdown.tsx +11 -0
  44. package/apps/dashboard/components/MainNav.tsx +38 -73
  45. package/apps/dashboard/components/MessageBlock.tsx +468 -0
  46. package/apps/dashboard/components/ModeStartCard.tsx +15 -16
  47. package/apps/dashboard/components/OnboardingWelcome.tsx +213 -0
  48. package/apps/dashboard/components/PlaceholderCard.tsx +3 -4
  49. package/apps/dashboard/components/ProjectSwitcher.tsx +30 -30
  50. package/apps/dashboard/components/PrototypeTimeline.tsx +72 -51
  51. package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +406 -388
  52. package/apps/dashboard/components/RealTimeTestsWrapper.tsx +373 -235
  53. package/apps/dashboard/components/ReviewFooter.tsx +139 -0
  54. package/apps/dashboard/components/SessionList.tsx +19 -19
  55. package/apps/dashboard/components/SubscribeContent.tsx +91 -47
  56. package/apps/dashboard/components/TestTree.tsx +16 -16
  57. package/apps/dashboard/components/TipCard.tsx +16 -17
  58. package/apps/dashboard/components/Toast.tsx +5 -6
  59. package/apps/dashboard/components/TypeIcon.tsx +55 -0
  60. package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
  61. package/apps/dashboard/components/WaveCompletionAnimation.tsx +52 -65
  62. package/apps/dashboard/components/WelcomeScreen.tsx +19 -35
  63. package/apps/dashboard/components/WorkItemHeader.tsx +4 -5
  64. package/apps/dashboard/components/WorkItemTree.tsx +11 -32
  65. package/apps/dashboard/components/settings/AccountSection.tsx +55 -35
  66. package/apps/dashboard/components/settings/AiContextSection.tsx +89 -0
  67. package/apps/dashboard/components/settings/ContextDocumentsSection.tsx +317 -0
  68. package/apps/dashboard/components/settings/EnvVarsSection.tsx +74 -152
  69. package/apps/dashboard/components/settings/GeneralSection.tsx +162 -56
  70. package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
  71. package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -5
  72. package/apps/dashboard/components/ui/Button.tsx +104 -0
  73. package/apps/dashboard/components/ui/Input.tsx +78 -0
  74. package/apps/dashboard/components.json +1 -1
  75. package/apps/dashboard/contexts/ClaudeSessionContext.tsx +711 -418
  76. package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -5
  77. package/apps/dashboard/contexts/UsageContext.tsx +87 -32
  78. package/apps/dashboard/dev.sh +35 -0
  79. package/apps/dashboard/eslint.config.mjs +9 -9
  80. package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
  81. package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
  82. package/apps/dashboard/hooks/useWebSocket.ts +138 -83
  83. package/apps/dashboard/index.html +73 -0
  84. package/apps/dashboard/lib/constants.ts +43 -0
  85. package/apps/dashboard/lib/data-bridge.ts +722 -0
  86. package/apps/dashboard/lib/db.ts +69 -1265
  87. package/apps/dashboard/lib/environment-config.ts +173 -0
  88. package/apps/dashboard/lib/environment-verification.ts +119 -0
  89. package/apps/dashboard/lib/kanban-utils.ts +270 -0
  90. package/apps/dashboard/lib/proof-run.ts +495 -0
  91. package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
  92. package/apps/dashboard/lib/run-migrations.js +27 -2
  93. package/apps/dashboard/lib/service-recovery.ts +326 -0
  94. package/apps/dashboard/lib/session-state-machine.ts +1 -0
  95. package/apps/dashboard/lib/session-state-utils.ts +0 -164
  96. package/apps/dashboard/lib/session-stream-manager.ts +308 -134
  97. package/apps/dashboard/lib/shadows.ts +7 -0
  98. package/apps/dashboard/lib/stream-manager-registry.ts +46 -6
  99. package/apps/dashboard/lib/tauri-bridge.ts +102 -0
  100. package/apps/dashboard/lib/tauri.ts +106 -0
  101. package/apps/dashboard/lib/utils.ts +6 -0
  102. package/apps/dashboard/next-env.d.ts +1 -1
  103. package/apps/dashboard/package.json +21 -32
  104. package/apps/dashboard/public/bug-icon.png +0 -0
  105. package/apps/dashboard/public/buoy-icon.png +0 -0
  106. package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
  107. package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
  108. package/apps/dashboard/public/in-flight-seagull.png +0 -0
  109. package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
  110. package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
  111. package/apps/dashboard/public/jettypod_logo.png +0 -0
  112. package/apps/dashboard/public/pier-icon.png +0 -0
  113. package/apps/dashboard/public/star-icon.png +0 -0
  114. package/apps/dashboard/public/wrench-icon.png +0 -0
  115. package/apps/dashboard/scripts/tauri-build.js +228 -0
  116. package/apps/dashboard/scripts/upload-tauri-to-r2.js +125 -0
  117. package/apps/dashboard/scripts/ws-server.js +191 -0
  118. package/apps/dashboard/src/main.tsx +12 -0
  119. package/apps/dashboard/src/router.tsx +107 -0
  120. package/apps/dashboard/src/vite-env.d.ts +1 -0
  121. package/apps/dashboard/tsconfig.json +7 -12
  122. package/apps/dashboard/tsconfig.tsbuildinfo +1 -1
  123. package/apps/dashboard/vite.config.ts +33 -0
  124. package/apps/update-server/src/index.ts +228 -80
  125. package/claude-hooks/global-guardrails.js +14 -13
  126. package/crates/jettypod-cli/Cargo.toml +19 -0
  127. package/crates/jettypod-cli/src/commands.rs +1249 -0
  128. package/crates/jettypod-cli/src/main.rs +595 -0
  129. package/crates/jettypod-core/Cargo.toml +26 -0
  130. package/crates/jettypod-core/build.rs +98 -0
  131. package/crates/jettypod-core/migrations/V1__baseline.sql +197 -0
  132. package/crates/jettypod-core/migrations/V2__work_items_indexes.sql +6 -0
  133. package/crates/jettypod-core/migrations/V3__qa_steps.sql +2 -0
  134. package/crates/jettypod-core/src/auth.rs +294 -0
  135. package/crates/jettypod-core/src/config.rs +397 -0
  136. package/crates/jettypod-core/src/db/mod.rs +507 -0
  137. package/crates/jettypod-core/src/db/recovery.rs +114 -0
  138. package/crates/jettypod-core/src/db/startup.rs +101 -0
  139. package/crates/jettypod-core/src/db/validate.rs +149 -0
  140. package/crates/jettypod-core/src/error.rs +76 -0
  141. package/crates/jettypod-core/src/git.rs +458 -0
  142. package/crates/jettypod-core/src/lib.rs +20 -0
  143. package/crates/jettypod-core/src/sessions.rs +625 -0
  144. package/crates/jettypod-core/src/skills.rs +556 -0
  145. package/crates/jettypod-core/src/work.rs +1086 -0
  146. package/crates/jettypod-core/src/worktree.rs +628 -0
  147. package/crates/jettypod-core/src/ws.rs +767 -0
  148. package/cucumber-test.cjs +6 -0
  149. package/cucumber.js +9 -3
  150. package/docs/COMMAND_REFERENCE.md +34 -0
  151. package/hooks/post-checkout +32 -75
  152. package/hooks/post-merge +111 -10
  153. package/jest.setup.js +1 -0
  154. package/jettypod.js +145 -116
  155. package/lib/bdd-preflight.js +96 -0
  156. package/lib/chore-taxonomy.js +33 -10
  157. package/lib/database.js +36 -16
  158. package/lib/db-watcher.js +1 -1
  159. package/lib/git-hooks/pre-commit +1 -1
  160. package/lib/jettypod-backup.js +27 -4
  161. package/lib/merge-lock.js +111 -253
  162. package/lib/migrations/027-plan-at-creation-column.js +3 -1
  163. package/lib/migrations/029-remove-autoincrement.js +307 -0
  164. package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
  165. package/lib/migrations/030-rejection-round-columns.js +54 -0
  166. package/lib/migrations/031-session-isolation-index.js +17 -0
  167. package/lib/migrations/index.js +47 -4
  168. package/lib/schema.js +10 -5
  169. package/lib/seed-onboarding.js +1 -1
  170. package/lib/update-command/index.js +9 -175
  171. package/lib/work-commands/index.js +144 -19
  172. package/lib/work-tracking/index.js +148 -27
  173. package/lib/worktree-diagnostics.js +16 -16
  174. package/lib/worktree-facade.js +1 -1
  175. package/lib/worktree-manager.js +8 -8
  176. package/lib/worktree-reconciler.js +5 -5
  177. package/package.json +9 -2
  178. package/scripts/ndjson-to-cucumber-json.js +152 -0
  179. package/scripts/postinstall.js +25 -0
  180. package/skills-templates/bug-mode/SKILL.md +79 -20
  181. package/skills-templates/bug-planning/SKILL.md +25 -29
  182. package/skills-templates/chore-mode/SKILL.md +171 -69
  183. package/skills-templates/chore-mode/verification.js +51 -10
  184. package/skills-templates/chore-planning/SKILL.md +47 -18
  185. package/skills-templates/design-system-selection/SKILL.md +273 -0
  186. package/skills-templates/epic-planning/SKILL.md +82 -48
  187. package/skills-templates/external-transition/SKILL.md +47 -47
  188. package/skills-templates/feature-planning/SKILL.md +173 -74
  189. package/skills-templates/production-mode/SKILL.md +69 -49
  190. package/skills-templates/request-routing/SKILL.md +4 -4
  191. package/skills-templates/simple-improvement/SKILL.md +74 -29
  192. package/skills-templates/speed-mode/SKILL.md +217 -141
  193. package/skills-templates/stable-mode/SKILL.md +148 -89
  194. package/apps/dashboard/README.md +0 -36
  195. package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -386
  196. package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
  197. package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -167
  198. package/apps/dashboard/app/api/claude/sessions/[sessionId]/content/route.ts +0 -52
  199. package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +0 -378
  200. package/apps/dashboard/app/api/claude/sessions/[sessionId]/pin/route.ts +0 -24
  201. package/apps/dashboard/app/api/claude/sessions/cleanup/route.ts +0 -34
  202. package/apps/dashboard/app/api/claude/sessions/route.ts +0 -184
  203. package/apps/dashboard/app/api/decisions/[id]/route.ts +0 -25
  204. package/apps/dashboard/app/api/internal/set-project/route.ts +0 -17
  205. package/apps/dashboard/app/api/kanban/route.ts +0 -15
  206. package/apps/dashboard/app/api/settings/env-vars/route.ts +0 -125
  207. package/apps/dashboard/app/api/settings/general/route.ts +0 -21
  208. package/apps/dashboard/app/api/tests/route.ts +0 -9
  209. package/apps/dashboard/app/api/tests/run/route.ts +0 -82
  210. package/apps/dashboard/app/api/tests/run/stream/route.ts +0 -71
  211. package/apps/dashboard/app/api/tests/undefined/route.ts +0 -9
  212. package/apps/dashboard/app/api/usage/route.ts +0 -17
  213. package/apps/dashboard/app/api/work/[id]/description/route.ts +0 -21
  214. package/apps/dashboard/app/api/work/[id]/epic/route.ts +0 -21
  215. package/apps/dashboard/app/api/work/[id]/order/route.ts +0 -21
  216. package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -21
  217. package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
  218. package/apps/dashboard/app/layout.tsx +0 -43
  219. package/apps/dashboard/components/UpgradeBanner.tsx +0 -29
  220. package/apps/dashboard/electron/ipc-handlers.js +0 -1028
  221. package/apps/dashboard/electron/main.js +0 -2124
  222. package/apps/dashboard/electron/preload.js +0 -123
  223. package/apps/dashboard/electron/session-manager.js +0 -141
  224. package/apps/dashboard/electron-builder.config.js +0 -357
  225. package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
  226. package/apps/dashboard/lib/claude-process-manager.ts +0 -492
  227. package/apps/dashboard/lib/db-bridge.ts +0 -282
  228. package/apps/dashboard/lib/prototypes.ts +0 -202
  229. package/apps/dashboard/lib/test-results-db.ts +0 -307
  230. package/apps/dashboard/lib/tests.ts +0 -282
  231. package/apps/dashboard/next.config.js +0 -50
  232. package/apps/dashboard/postcss.config.mjs +0 -7
  233. package/apps/dashboard/public/file.svg +0 -1
  234. package/apps/dashboard/public/globe.svg +0 -1
  235. package/apps/dashboard/public/next.svg +0 -1
  236. package/apps/dashboard/public/vercel.svg +0 -1
  237. package/apps/dashboard/public/window.svg +0 -1
  238. package/apps/dashboard/scripts/download-node.js +0 -104
  239. package/apps/dashboard/scripts/upload-to-r2.js +0 -89
  240. package/docs/bdd-guidance.md +0 -390
@@ -1,30 +1,9 @@
1
- 'use client';
2
-
3
1
  import { useState } from 'react';
4
- import Link from 'next/link';
2
+ import { Link } from 'react-router-dom';
5
3
  import type { WorkItem } from '@/lib/db';
6
4
  import { CopyableId } from './CopyableId';
7
-
8
- const typeIcons: Record<string, string> = {
9
- epic: '🎯',
10
- feature: '✨',
11
- chore: '🔧',
12
- bug: '🐛',
13
- };
14
-
15
- const statusColors: Record<string, string> = {
16
- backlog: 'text-zinc-500',
17
- todo: 'text-zinc-500',
18
- in_progress: 'text-blue-600 dark:text-blue-400',
19
- done: 'text-green-600 dark:text-green-400',
20
- cancelled: 'text-red-500',
21
- };
22
-
23
- const modeLabels: Record<string, { label: string; color: string }> = {
24
- speed: { label: 'speed', color: 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200' },
25
- stable: { label: 'stable', color: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200' },
26
- production: { label: 'prod', color: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200' },
27
- };
5
+ import { STATUS_COLORS, MODE_LABELS } from '@/lib/constants';
6
+ import { TypeIcon } from './TypeIcon';
28
7
 
29
8
  interface WorkItemNodeProps {
30
9
  item: WorkItem;
@@ -38,7 +17,7 @@ function WorkItemNode({ item, depth = 0 }: WorkItemNodeProps) {
38
17
  return (
39
18
  <div className="select-none">
40
19
  <div
41
- className={`flex items-center gap-2 py-1.5 px-2 rounded hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors group`}
20
+ className={`flex items-center gap-3 py-2 px-3 rounded hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors duration-200 ease-out group`}
42
21
  style={{ paddingLeft: `${depth * 20 + 8}px` }}
43
22
  >
44
23
  {/* Expand/collapse toggle */}
@@ -54,23 +33,23 @@ function WorkItemNode({ item, depth = 0 }: WorkItemNodeProps) {
54
33
  )}
55
34
 
56
35
  {/* Type icon */}
57
- <span className="text-sm">{typeIcons[item.type] || '📄'}</span>
36
+ <span className="text-base"><TypeIcon type={item.type} /></span>
58
37
 
59
38
  {/* ID with click-to-copy */}
60
39
  <CopyableId id={item.id} title={item.title} type={item.type} />
61
40
 
62
41
  {/* Title */}
63
42
  <Link
64
- href={`/work/${item.id}`}
65
- className={`flex-1 truncate hover:underline ${statusColors[item.status]}`}
43
+ to={`/work/${item.id}`}
44
+ className={`flex-1 truncate hover:underline ${STATUS_COLORS[item.status]}`}
66
45
  >
67
46
  {item.title}
68
47
  </Link>
69
48
 
70
49
  {/* Mode badge */}
71
- {item.mode && modeLabels[item.mode] && (
72
- <span className={`text-xs px-1.5 py-0.5 rounded ${modeLabels[item.mode].color}`}>
73
- {modeLabels[item.mode].label}
50
+ {item.mode && MODE_LABELS[item.mode] && (
51
+ <span className={`text-xs px-2 py-1 rounded ${MODE_LABELS[item.mode].color}`}>
52
+ {MODE_LABELS[item.mode].label}
74
53
  </span>
75
54
  )}
76
55
 
@@ -108,7 +87,7 @@ export function WorkItemTree({ items }: WorkItemTreeProps) {
108
87
  }
109
88
 
110
89
  return (
111
- <div className="font-mono text-sm">
90
+ <div className="font-mono text-base">
112
91
  {items.map((item) => (
113
92
  <WorkItemNode key={item.id} item={item} />
114
93
  ))}
@@ -1,18 +1,20 @@
1
- 'use client';
2
-
3
1
  import { useState, useEffect } from 'react';
4
- import { useRouter } from 'next/navigation';
2
+ import { useNavigate } from 'react-router-dom';
5
3
  import { useUsage } from '@/contexts/UsageContext';
6
4
  import { SubscribeContent } from '@/components/SubscribeContent';
5
+ import { Button } from '@/components/ui/Button';
6
+ import { isTauri, auth, shell } from '@/lib/tauri-bridge';
7
+
8
+ const API_BASE = 'https://jettypod-update-server.spangbaryn2.workers.dev';
7
9
 
8
10
  const planColors: Record<string, string> = {
9
11
  free: 'bg-zinc-200 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-300',
10
- monthly: 'bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300',
12
+ monthly: 'bg-[#e8f0f0] dark:bg-[#819D9F]/20 text-[#5a7d7f] dark:text-[#a3bfc0]',
11
13
  lifetime: 'bg-purple-100 dark:bg-purple-900/40 text-purple-700 dark:text-purple-300',
12
14
  };
13
15
 
14
16
  export function AccountSection() {
15
- const router = useRouter();
17
+ const navigate = useNavigate();
16
18
  const { plan, used, limit, loading, refresh } = useUsage();
17
19
  const [email, setEmail] = useState<string | null>(null);
18
20
  const [portalError, setPortalError] = useState<string | null>(null);
@@ -21,9 +23,9 @@ export function AccountSection() {
21
23
 
22
24
  useEffect(() => {
23
25
  async function loadAuth() {
24
- if (!window.electronAPI?.isElectron) return;
26
+ if (!isTauri()) return;
25
27
  try {
26
- const status = await window.electronAPI.auth.getStatus();
28
+ const status = await auth.getStatus();
27
29
  if (status.authenticated && status.user) {
28
30
  setEmail(status.user.email);
29
31
  }
@@ -38,13 +40,27 @@ export function AccountSection() {
38
40
  const isAtCapacity = isFree && limit > 0 && used >= limit;
39
41
 
40
42
  const handleManageSubscription = async () => {
41
- if (!window.electronAPI?.billing?.openCustomerPortal) return;
42
43
  setPortalError(null);
43
44
  setPortalLoading(true);
44
45
  try {
45
- const result = await window.electronAPI.billing.openCustomerPortal();
46
- if (result && !result.success) {
47
- setPortalError(result.error || 'Unable to open billing portal');
46
+ const token = await auth.getToken();
47
+ if (!token) {
48
+ setPortalError('Not authenticated');
49
+ return;
50
+ }
51
+ const res = await fetch(`${API_BASE}/billing/customer-portal`, {
52
+ method: 'POST',
53
+ headers: { 'Authorization': `Bearer ${token}` },
54
+ });
55
+ if (res.ok) {
56
+ const data = await res.json() as { url?: string };
57
+ if (data.url) {
58
+ await shell.openUrl(data.url);
59
+ } else {
60
+ setPortalError('Unable to open billing portal');
61
+ }
62
+ } else {
63
+ setPortalError('Unable to open billing portal');
48
64
  }
49
65
  } catch {
50
66
  setPortalError('Unable to open billing portal');
@@ -54,9 +70,8 @@ export function AccountSection() {
54
70
  };
55
71
 
56
72
  const handleLogout = async () => {
57
- if (!window.electronAPI?.auth?.logout) return;
58
- await window.electronAPI.auth.logout();
59
- router.push('/login');
73
+ await auth.logout();
74
+ navigate('/login');
60
75
  };
61
76
 
62
77
  const handleUpgradeClose = () => {
@@ -67,15 +82,17 @@ export function AccountSection() {
67
82
  if (showUpgrade) {
68
83
  return (
69
84
  <section id="account" className="relative min-h-[500px]">
70
- <button
85
+ <Button
71
86
  onClick={handleUpgradeClose}
72
- className="absolute top-0 right-0 p-1.5 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 transition-colors z-10"
87
+ variant="ghost"
88
+ size="icon"
89
+ className="absolute top-0 right-0 z-10"
73
90
  aria-label="Close upgrade"
74
91
  >
75
92
  <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
76
93
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
77
94
  </svg>
78
- </button>
95
+ </Button>
79
96
  <div className="flex flex-col items-center justify-center min-h-[500px] py-8">
80
97
  <SubscribeContent onClose={handleUpgradeClose} />
81
98
  </div>
@@ -85,25 +102,25 @@ export function AccountSection() {
85
102
 
86
103
  return (
87
104
  <section id="account">
88
- <div className="flex items-center justify-between mb-4">
105
+ <div className="flex items-center justify-between mb-6">
89
106
  <h2 className="text-lg font-medium text-zinc-900 dark:text-zinc-100">
90
107
  Account
91
108
  </h2>
92
109
  </div>
93
110
 
94
111
  {/* Email & Plan */}
95
- <div className="p-4 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg space-y-4">
112
+ <div className="p-6 bg-zinc-50 dark:bg-zinc-800/50 rounded-xl space-y-4">
96
113
  <div className="flex items-center justify-between">
97
114
  <div>
98
- <label className="block text-sm font-medium text-zinc-900 dark:text-zinc-100">
115
+ <label className="block text-base font-medium text-zinc-900 dark:text-zinc-100">
99
116
  Email
100
117
  </label>
101
- <p className="text-sm text-zinc-600 dark:text-zinc-400 mt-0.5">
118
+ <p className="text-base text-zinc-600 dark:text-zinc-400 mt-1">
102
119
  {loading ? '...' : email || 'Not signed in'}
103
120
  </p>
104
121
  </div>
105
- <div className="flex items-center gap-2">
106
- <span className={`px-2 py-0.5 text-xs font-medium rounded-full ${planColors[plan] || planColors.free}`}>
122
+ <div className="flex items-center gap-3">
123
+ <span className={`px-2 py-1 text-xs font-medium rounded-full ${planColors[plan] || planColors.free}`}>
107
124
  {plan}
108
125
  </span>
109
126
  </div>
@@ -112,13 +129,13 @@ export function AccountSection() {
112
129
  {/* Usage Bar (free users only) */}
113
130
  {isFree && (
114
131
  <div>
115
- <div className="flex items-center justify-between text-xs text-zinc-500 dark:text-zinc-400 mb-1">
132
+ <div className="flex items-center justify-between text-base text-zinc-500 dark:text-zinc-400 mb-1.5">
116
133
  <span>Weekly usage</span>
117
134
  <span>{used} / {limit} work items</span>
118
135
  </div>
119
136
  <div className="w-full h-2 bg-zinc-200 dark:bg-zinc-700 rounded-full overflow-hidden">
120
137
  <div
121
- className={`h-full rounded-full transition-all ${isAtCapacity ? 'bg-red-500' : 'bg-blue-500'}`}
138
+ className={`h-full rounded-full transition-[width,background-color] duration-200 ease-out ${isAtCapacity ? 'bg-red-500' : 'bg-[#819D9F]'}`}
122
139
  style={{ width: `${limit > 0 ? Math.min((used / limit) * 100, 100) : 0}%` }}
123
140
  />
124
141
  </div>
@@ -129,33 +146,36 @@ export function AccountSection() {
129
146
  <div className="pt-2 border-t border-zinc-200 dark:border-zinc-700 flex items-center justify-between">
130
147
  <div>
131
148
  {isFree ? (
132
- <button
149
+ <Button
133
150
  onClick={() => setShowUpgrade(true)}
134
- className="px-3 py-1.5 text-sm font-medium bg-blue-600 hover:bg-blue-500 text-white rounded transition-colors"
151
+ variant="accent"
152
+ size="sm"
135
153
  >
136
154
  Upgrade
137
- </button>
155
+ </Button>
138
156
  ) : (
139
157
  <>
140
- <button
158
+ <Button
141
159
  onClick={handleManageSubscription}
142
160
  disabled={portalLoading}
143
- className="px-3 py-1.5 text-sm font-medium text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100 border border-zinc-300 dark:border-zinc-600 rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
161
+ variant="secondary"
162
+ size="sm"
144
163
  >
145
164
  {portalLoading ? 'Opening...' : 'Manage Subscription'}
146
- </button>
165
+ </Button>
147
166
  {portalError && (
148
- <p className="text-xs text-red-500 mt-1">{portalError}</p>
167
+ <p className="text-base text-red-500 mt-1">{portalError}</p>
149
168
  )}
150
169
  </>
151
170
  )}
152
171
  </div>
153
- <button
172
+ <Button
154
173
  onClick={handleLogout}
155
- className="px-3 py-1.5 text-sm font-medium text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 transition-colors"
174
+ variant="destructive"
175
+ size="sm"
156
176
  >
157
177
  Log Out
158
- </button>
178
+ </Button>
159
179
  </div>
160
180
  </div>
161
181
  </section>
@@ -0,0 +1,89 @@
1
+ import { useState } from 'react';
2
+ import { invoke } from '@/lib/tauri';
3
+ import { openDialog } from '@/lib/tauri';
4
+ import { Button } from '@/components/ui/Button';
5
+ import { ContextDocumentsSection } from './ContextDocumentsSection';
6
+ import type { ContextDocument } from '@/lib/data-bridge';
7
+
8
+ interface AiContextSectionProps {
9
+ initialDesignSystemDir: string | null;
10
+ initialContextDocuments: ContextDocument[];
11
+ }
12
+
13
+ export function AiContextSection({ initialDesignSystemDir, initialContextDocuments }: AiContextSectionProps) {
14
+ const [designSystemDir, setDesignSystemDir] = useState<string | null>(initialDesignSystemDir);
15
+ const [successMessage, setSuccessMessage] = useState<string | null>(null);
16
+
17
+ const showSuccess = (message: string) => {
18
+ setSuccessMessage(message);
19
+ setTimeout(() => setSuccessMessage(null), 3000);
20
+ };
21
+
22
+ const handleBrowse = async () => {
23
+ const selected = await openDialog({ directory: true, title: 'Select Design System Directory' });
24
+ if (selected && typeof selected === 'string') {
25
+ await invoke('db_set_design_system_dir', { dir: selected });
26
+ setDesignSystemDir(selected);
27
+ showSuccess('Design system directory set');
28
+ }
29
+ };
30
+
31
+ const handleClear = async () => {
32
+ await invoke('db_reset_design_system_dir');
33
+ setDesignSystemDir(null);
34
+ showSuccess('Design system directory cleared');
35
+ };
36
+
37
+ return (
38
+ <section id="ai-context">
39
+ <div className="flex items-center justify-between mb-6">
40
+ <h2 className="text-lg font-medium text-zinc-900 dark:text-zinc-100">
41
+ AI Context
42
+ </h2>
43
+ </div>
44
+
45
+ {successMessage && (
46
+ <div className="mb-4 px-6 py-3 bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 rounded-lg text-base">
47
+ {successMessage}
48
+ </div>
49
+ )}
50
+
51
+ {/* Design System Directory Setting */}
52
+ <div className="p-6 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg">
53
+ <div>
54
+ <label className="block text-base font-medium text-zinc-900 dark:text-zinc-100">
55
+ Design System Directory
56
+ </label>
57
+ <p className="text-base text-zinc-500 dark:text-zinc-400 mt-1">
58
+ Select a folder that includes your design system. Claude will use this when prototyping and building parts of your project.
59
+ </p>
60
+ </div>
61
+
62
+ <div className="mt-4">
63
+ {designSystemDir ? (
64
+ <div className="flex items-center gap-3">
65
+ <span className="font-mono text-sm text-zinc-900 dark:text-zinc-100 bg-zinc-200 dark:bg-zinc-700 px-3 py-2 rounded flex-1 truncate">
66
+ {designSystemDir}
67
+ </span>
68
+ <Button onClick={handleBrowse} variant="ghost" size="sm">
69
+ Change
70
+ </Button>
71
+ <Button onClick={handleClear} variant="ghost" size="sm">
72
+ Clear
73
+ </Button>
74
+ </div>
75
+ ) : (
76
+ <div className="flex items-center gap-3">
77
+ <Button onClick={handleBrowse} size="sm">
78
+ Browse...
79
+ </Button>
80
+ </div>
81
+ )}
82
+ </div>
83
+ </div>
84
+
85
+ {/* Context Documents */}
86
+ <ContextDocumentsSection initialDocuments={initialContextDocuments} />
87
+ </section>
88
+ );
89
+ }