@assistkick/create 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/dist/bin/create.js +0 -0
  2. package/package.json +9 -7
  3. package/templates/assistkick-product-system/.env.example +1 -0
  4. package/templates/assistkick-product-system/local.db +0 -0
  5. package/templates/assistkick-product-system/package.json +4 -2
  6. package/templates/assistkick-product-system/packages/backend/package.json +2 -0
  7. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
  9. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
  10. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
  11. package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
  12. package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +43 -4
  13. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
  14. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
  15. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
  16. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
  17. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
  18. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +154 -0
  19. package/templates/assistkick-product-system/packages/backend/src/server.ts +81 -9
  20. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
  21. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
  22. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
  23. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
  24. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +28 -78
  25. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
  26. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
  27. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
  28. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
  29. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
  30. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
  31. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
  32. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
  33. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
  34. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
  35. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
  40. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +222 -0
  41. package/templates/assistkick-product-system/packages/frontend/package-lock.json +3455 -0
  42. package/templates/assistkick-product-system/packages/frontend/package.json +6 -0
  43. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +8 -0
  44. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +456 -16
  45. package/templates/assistkick-product-system/packages/frontend/src/api/client_files.test.ts +172 -0
  46. package/templates/assistkick-product-system/packages/frontend/src/api/client_video.test.ts +238 -0
  47. package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +307 -0
  48. package/templates/assistkick-product-system/packages/frontend/src/components/CoherenceView.tsx +82 -66
  49. package/templates/assistkick-product-system/packages/frontend/src/components/CompositionPlaceholder.tsx +97 -0
  50. package/templates/assistkick-product-system/packages/frontend/src/components/DesignSystemView.tsx +20 -0
  51. package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +57 -0
  52. package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +313 -0
  53. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeContextMenu.tsx +61 -0
  54. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +73 -0
  55. package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +404 -0
  56. package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +187 -56
  57. package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +71 -73
  58. package/templates/assistkick-product-system/packages/frontend/src/components/GraphSettings.tsx +8 -8
  59. package/templates/assistkick-product-system/packages/frontend/src/components/GraphView.tsx +1 -1
  60. package/templates/assistkick-product-system/packages/frontend/src/components/InviteUserDialog.tsx +15 -11
  61. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +202 -171
  62. package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
  63. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
  64. package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +32 -49
  65. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +43 -48
  66. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
  67. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +20 -14
  68. package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
  69. package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
  70. package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
  71. package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +103 -87
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +9 -188
  82. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
  83. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +81 -37
  85. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
  86. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
  87. package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
  88. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
  89. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
  92. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
  97. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +335 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +634 -0
  99. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +208 -0
  102. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +107 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
  105. package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
  106. package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
  107. package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
  108. package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
  109. package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
  110. package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
  111. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
  112. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +1 -1
  113. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +2 -2
  114. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
  115. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -2
  116. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
  117. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
  118. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +6 -3
  119. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +4 -4
  120. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +275 -3535
  121. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
  123. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
  124. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
  125. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
  128. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
  129. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +5 -0
  130. package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
  131. package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
  132. package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
  133. package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
  134. package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
  135. package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
  136. package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
  137. package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
  138. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
  139. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
  140. package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
  141. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
  142. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
  143. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
  144. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
  145. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
  146. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
  147. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
  148. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
  149. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
  150. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
  151. package/templates/assistkick-product-system/packages/shared/db/schema.ts +113 -0
  152. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
  153. package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
  154. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
  155. package/templates/assistkick-product-system/packages/shared/lib/graph.ts +5 -0
  156. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1753 -0
  157. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1281 -0
  158. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
  159. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
  160. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
  161. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
  162. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
  163. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
  164. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  165. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
  166. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
  167. package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
  168. package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
  169. package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
  170. package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
  171. package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
  172. package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
  173. package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
  174. package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
  175. package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
  176. package/templates/assistkick-product-system/packages/video/index.ts +4 -0
  177. package/templates/assistkick-product-system/packages/video/package.json +28 -0
  178. package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
  179. package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
  180. package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
  181. package/templates/assistkick-product-system/packages/video/style.css +1 -0
  182. package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
  183. package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
  184. package/templates/assistkick-product-system/tests/video_render_service.test.ts +179 -0
  185. package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
  186. package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
  187. package/templates/skills/assistkick-developer/SKILL.md +3 -0
  188. package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
  189. package/templates/skills/product-system/graph.json +1890 -0
  190. package/templates/skills/product-system/kanban.json +304 -0
  191. package/templates/skills/product-system/nodes/comp_001.md +56 -0
  192. package/templates/skills/product-system/nodes/comp_002.md +57 -0
  193. package/templates/skills/product-system/nodes/data_001.md +51 -0
  194. package/templates/skills/product-system/nodes/data_002.md +40 -0
  195. package/templates/skills/product-system/nodes/data_004.md +38 -0
  196. package/templates/skills/product-system/nodes/dec_001.md +34 -0
  197. package/templates/skills/product-system/nodes/dec_016.md +32 -0
  198. package/templates/skills/product-system/nodes/feat_008.md +30 -0
  199. package/templates/skills/video-composition-agent/SKILL.md +232 -0
  200. package/templates/skills/video-script-writer/SKILL.md +136 -0
@@ -17,7 +17,16 @@ interface Repo {
17
17
  cloneUrl: string;
18
18
  }
19
19
 
20
- type Tab = 'status' | 'github' | 'url' | 'init';
20
+ type Tab = 'status' | 'github' | 'ssh' | 'url' | 'init';
21
+
22
+ const tabCls = (active: boolean) =>
23
+ `bg-none border-none cursor-pointer font-mono text-xs px-3 py-2.5 border-b-2 ${
24
+ active ? 'text-accent border-b-accent' : 'text-content-secondary border-b-transparent hover:text-content'
25
+ }`;
26
+
27
+ const btnBase = 'inline-block px-4 py-2 border border-edge rounded bg-surface-raised text-content font-mono text-xs cursor-pointer mt-3 hover:bg-tab-hover disabled:opacity-50 disabled:cursor-not-allowed';
28
+ const btnPrimary = 'inline-block px-4 py-2 rounded font-mono text-xs cursor-pointer mt-3 bg-accent text-white border border-accent hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed';
29
+ const btnDanger = 'inline-block px-4 py-2 rounded font-mono text-xs cursor-pointer mt-4 bg-transparent border border-error text-error hover:bg-[rgba(255,107,107,0.1)] disabled:opacity-50 disabled:cursor-not-allowed';
21
30
 
22
31
  export function GitRepoModal() {
23
32
  const project = useGitModalStore((s) => s.project)!;
@@ -28,24 +37,27 @@ export function GitRepoModal() {
28
37
  const [error, setError] = useState<string | null>(null);
29
38
  const [success, setSuccess] = useState<string | null>(null);
30
39
 
31
- // Status tab
32
40
  const [gitStatus, setGitStatus] = useState<any>(null);
33
41
 
34
- // GitHub tab
35
42
  const [installations, setInstallations] = useState<Installation[]>([]);
36
43
  const [selectedInstallation, setSelectedInstallation] = useState<string>('');
37
44
  const [repos, setRepos] = useState<Repo[]>([]);
38
45
  const [selectedRepo, setSelectedRepo] = useState<string>('');
39
46
  const [githubConfigured, setGithubConfigured] = useState(false);
40
47
 
41
- // URL tab
42
48
  const [cloneUrl, setCloneUrl] = useState('');
43
49
 
50
+ // SSH state
51
+ const [sshPublicKey, setSshPublicKey] = useState<string | null>(null);
52
+ const [sshCloneUrl, setSshCloneUrl] = useState('');
53
+ const [copied, setCopied] = useState(false);
54
+
44
55
  const loadStatus = useCallback(async () => {
45
56
  try {
46
57
  const data = await apiClient.getGitStatus(project.id);
47
58
  setGitStatus(data);
48
59
  setGithubConfigured(data.githubAppConfigured);
60
+ if (data.sshPublicKey) setSshPublicKey(data.sshPublicKey);
49
61
  } catch (err: any) {
50
62
  setError(err.message);
51
63
  }
@@ -163,6 +175,50 @@ export function GitRepoModal() {
163
175
  try {
164
176
  await apiClient.disconnectGitRepo(project.id);
165
177
  setSuccess('Repository disconnected');
178
+ setSshPublicKey(null);
179
+ onProjectUpdated();
180
+ await loadStatus();
181
+ } catch (err: any) {
182
+ setError(err.message);
183
+ } finally {
184
+ setLoading(false);
185
+ }
186
+ };
187
+
188
+ const handleGenerateSshKey = async () => {
189
+ setLoading(true);
190
+ setError(null);
191
+ setCopied(false);
192
+ try {
193
+ const data = await apiClient.generateSshKey(project.id);
194
+ setSshPublicKey(data.publicKey);
195
+ setSuccess(sshPublicKey ? 'SSH keypair regenerated — old key is now invalid' : 'SSH keypair generated');
196
+ onProjectUpdated();
197
+ } catch (err: any) {
198
+ setError(err.message);
199
+ } finally {
200
+ setLoading(false);
201
+ }
202
+ };
203
+
204
+ const handleCopyPublicKey = async () => {
205
+ if (!sshPublicKey) return;
206
+ try {
207
+ await navigator.clipboard.writeText(sshPublicKey);
208
+ setCopied(true);
209
+ setTimeout(() => setCopied(false), 2000);
210
+ } catch {
211
+ setError('Failed to copy to clipboard');
212
+ }
213
+ };
214
+
215
+ const handleConnectSsh = async () => {
216
+ if (!sshCloneUrl.trim()) return;
217
+ setLoading(true);
218
+ setError(null);
219
+ try {
220
+ await apiClient.connectSshRepo(project.id, sshCloneUrl.trim());
221
+ setSuccess('Repository connected via SSH');
166
222
  onProjectUpdated();
167
223
  await loadStatus();
168
224
  } catch (err: any) {
@@ -173,98 +229,105 @@ export function GitRepoModal() {
173
229
  };
174
230
 
175
231
  return (
176
- <div className="git-modal-overlay" onClick={onClose}>
177
- <div className="git-modal" onClick={e => e.stopPropagation()}>
178
- <div className="git-modal-header">
179
- <h3>Git Repository &mdash; {project.name}</h3>
180
- <button className="git-modal-close" onClick={onClose}>&times;</button>
232
+ <div className="fixed inset-0 bg-black/50 z-[1000] flex items-center justify-center" onClick={onClose}>
233
+ <div className="bg-surface-alt border border-edge rounded-lg w-[480px] max-w-[90vw] max-h-[80vh] overflow-y-auto shadow-[0_8px_32px_var(--panel-shadow)] font-mono text-[13px]" onClick={e => e.stopPropagation()}>
234
+ <div className="flex justify-between items-center px-5 pt-4 pb-3 border-b border-edge">
235
+ <h3 className="m-0 text-sm text-content font-semibold">Git Repository &mdash; {project.name}</h3>
236
+ <button className="bg-none border-none text-content-secondary cursor-pointer text-lg px-1 leading-none hover:text-content" onClick={onClose}>&times;</button>
181
237
  </div>
182
238
 
183
- {error && <div className="git-modal-error">{error}</div>}
184
- {success && <div className="git-modal-success">{success}</div>}
239
+ {error && <div className="px-5 py-2 text-error text-xs bg-[rgba(255,107,107,0.08)] border-b border-edge">{error}</div>}
240
+ {success && <div className="px-5 py-2 text-[#69db7c] text-xs bg-[rgba(105,219,124,0.08)] border-b border-edge">{success}</div>}
185
241
 
186
- <div className="git-modal-tabs">
187
- <button className={`git-modal-tab${tab === 'status' ? ' active' : ''}`} onClick={() => { setTab('status'); setError(null); setSuccess(null); }}>Status</button>
188
- <button className={`git-modal-tab${tab === 'github' ? ' active' : ''}`} onClick={() => { setTab('github'); setError(null); setSuccess(null); handleLoadInstallations(); }}>GitHub</button>
189
- <button className={`git-modal-tab${tab === 'url' ? ' active' : ''}`} onClick={() => { setTab('url'); setError(null); setSuccess(null); }}>Clone URL</button>
190
- <button className={`git-modal-tab${tab === 'init' ? ' active' : ''}`} onClick={() => { setTab('init'); setError(null); setSuccess(null); }}>Init</button>
242
+ <div className="flex border-b border-edge px-4">
243
+ <button className={tabCls(tab === 'status')} onClick={() => { setTab('status'); setError(null); setSuccess(null); }}>Status</button>
244
+ <button className={tabCls(tab === 'github')} onClick={() => { setTab('github'); setError(null); setSuccess(null); handleLoadInstallations(); }}>GitHub</button>
245
+ <button className={tabCls(tab === 'ssh')} onClick={() => { setTab('ssh'); setError(null); setSuccess(null); }}>SSH Key</button>
246
+ <button className={tabCls(tab === 'url')} onClick={() => { setTab('url'); setError(null); setSuccess(null); }}>Clone URL</button>
247
+ <button className={tabCls(tab === 'init')} onClick={() => { setTab('init'); setError(null); setSuccess(null); }}>Init</button>
191
248
  </div>
192
249
 
193
- <div className="git-modal-body">
250
+ <div className="px-5 py-4">
194
251
  {tab === 'status' && (
195
- <div className="git-modal-status">
252
+ <div>
196
253
  {gitStatus ? (
197
254
  <>
198
- <div className="git-status-row">
199
- <span className="git-status-label">Workspace:</span>
200
- <span className={`git-status-value ${gitStatus.hasWorkspace ? 'connected' : ''}`}>
255
+ <div className="flex justify-between py-1.5 border-b border-edge">
256
+ <span className="text-content-secondary text-xs">Workspace:</span>
257
+ <span className={`text-xs ${gitStatus.hasWorkspace ? 'text-[#69db7c]' : 'text-content'}`}>
201
258
  {gitStatus.hasWorkspace ? 'Configured' : 'Not configured'}
202
259
  </span>
203
260
  </div>
204
261
  {gitStatus.hasWorkspace && (
205
262
  <>
206
- <div className="git-status-row">
207
- <span className="git-status-label">Remote:</span>
208
- <span className={`git-status-value ${gitStatus.hasRemote ? 'connected' : ''}`}>
263
+ <div className="flex justify-between py-1.5 border-b border-edge">
264
+ <span className="text-content-secondary text-xs">Remote:</span>
265
+ <span className={`text-xs ${gitStatus.hasRemote ? 'text-[#69db7c]' : 'text-content'}`}>
209
266
  {gitStatus.hasRemote ? 'Connected' : 'Local only'}
210
267
  </span>
211
268
  </div>
212
- <div className="git-status-row">
213
- <span className="git-status-label">Branch:</span>
214
- <span className="git-status-value">{gitStatus.currentBranch || '-'}</span>
269
+ <div className="flex justify-between py-1.5 border-b border-edge">
270
+ <span className="text-content-secondary text-xs">Branch:</span>
271
+ <span className="text-xs text-content">{gitStatus.currentBranch || '-'}</span>
215
272
  </div>
216
273
  </>
217
274
  )}
218
275
  {project.githubRepoFullName && (
219
- <div className="git-status-row">
220
- <span className="git-status-label">GitHub Repo:</span>
221
- <span className="git-status-value">{project.githubRepoFullName}</span>
276
+ <div className="flex justify-between py-1.5 border-b border-edge">
277
+ <span className="text-content-secondary text-xs">GitHub Repo:</span>
278
+ <span className="text-xs text-content">{project.githubRepoFullName}</span>
222
279
  </div>
223
280
  )}
224
281
  {project.repoUrl && !project.githubRepoFullName && (
225
- <div className="git-status-row">
226
- <span className="git-status-label">Clone URL:</span>
227
- <span className="git-status-value git-status-url">{project.repoUrl}</span>
282
+ <div className="flex justify-between py-1.5 border-b border-edge">
283
+ <span className="text-content-secondary text-xs">Clone URL:</span>
284
+ <span className="text-xs text-content max-w-60 overflow-hidden text-ellipsis whitespace-nowrap">{project.repoUrl}</span>
228
285
  </div>
229
286
  )}
230
- <div className="git-status-row">
231
- <span className="git-status-label">GitHub App:</span>
232
- <span className={`git-status-value ${gitStatus.githubAppConfigured ? 'connected' : ''}`}>
287
+ <div className="flex justify-between py-1.5 border-b border-edge">
288
+ <span className="text-content-secondary text-xs">Auth Method:</span>
289
+ <span className="text-xs text-content">
290
+ {gitStatus.gitAuthMethod === 'ssh_key' ? 'SSH Key' : gitStatus.gitAuthMethod === 'github_app' ? 'GitHub App' : 'None'}
291
+ </span>
292
+ </div>
293
+ <div className="flex justify-between py-1.5 border-b border-edge">
294
+ <span className="text-content-secondary text-xs">GitHub App:</span>
295
+ <span className={`text-xs ${gitStatus.githubAppConfigured ? 'text-[#69db7c]' : 'text-content'}`}>
233
296
  {gitStatus.githubAppConfigured ? 'Configured' : 'Not configured'}
234
297
  </span>
235
298
  </div>
236
299
  {gitStatus.hasWorkspace && (
237
- <button className="git-modal-btn git-modal-btn-danger" onClick={handleDisconnect} disabled={loading}>
300
+ <button className={btnDanger} onClick={handleDisconnect} disabled={loading}>
238
301
  Disconnect
239
302
  </button>
240
303
  )}
241
304
  </>
242
305
  ) : (
243
- <div className="git-modal-loading">Loading...</div>
306
+ <div className="text-content-muted text-center py-5">Loading...</div>
244
307
  )}
245
308
  </div>
246
309
  )}
247
310
 
248
311
  {tab === 'github' && (
249
- <div className="git-modal-github">
312
+ <div>
250
313
  {!githubConfigured ? (
251
- <div className="git-modal-info">
314
+ <div className="text-content-secondary text-xs leading-relaxed mb-3">
252
315
  GitHub App not configured. Set GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY environment variables.
253
- <button className="git-modal-btn" onClick={handleTestConnection} disabled={loading}>
316
+ <button className={btnBase} onClick={handleTestConnection} disabled={loading}>
254
317
  Test Connection
255
318
  </button>
256
319
  </div>
257
320
  ) : (
258
321
  <>
259
322
  {installations.length === 0 ? (
260
- <div className="git-modal-info">
323
+ <div className="text-content-secondary text-xs leading-relaxed mb-3">
261
324
  No GitHub App installations found. Install the app on your GitHub organization or account.
262
325
  </div>
263
326
  ) : (
264
- <div className="git-modal-installations">
265
- <label className="git-modal-label">Installation:</label>
327
+ <div>
328
+ <label className="block text-content-secondary text-[11px] uppercase tracking-wider mb-1.5 mt-3">Installation:</label>
266
329
  <select
267
- className="git-modal-select"
330
+ className="w-full py-2 px-2.5 bg-surface-raised border border-edge rounded text-content font-mono text-xs"
268
331
  value={selectedInstallation}
269
332
  onChange={e => handleLoadRepos(e.target.value)}
270
333
  >
@@ -279,10 +342,10 @@ export function GitRepoModal() {
279
342
  )}
280
343
 
281
344
  {repos.length > 0 && (
282
- <div className="git-modal-repos">
283
- <label className="git-modal-label">Repository:</label>
345
+ <div>
346
+ <label className="block text-content-secondary text-[11px] uppercase tracking-wider mb-1.5 mt-3">Repository:</label>
284
347
  <select
285
- className="git-modal-select"
348
+ className="w-full py-2 px-2.5 bg-surface-raised border border-edge rounded text-content font-mono text-xs"
286
349
  value={selectedRepo}
287
350
  onChange={e => setSelectedRepo(e.target.value)}
288
351
  >
@@ -294,7 +357,7 @@ export function GitRepoModal() {
294
357
  ))}
295
358
  </select>
296
359
  <button
297
- className="git-modal-btn git-modal-btn-primary"
360
+ className={btnPrimary}
298
361
  onClick={handleConnectGitHub}
299
362
  disabled={loading || !selectedRepo}
300
363
  >
@@ -307,11 +370,79 @@ export function GitRepoModal() {
307
370
  </div>
308
371
  )}
309
372
 
373
+ {tab === 'ssh' && (
374
+ <div>
375
+ <p className="text-content-secondary text-xs leading-relaxed mb-3">
376
+ Generate an SSH deploy key for this project. Add the public key to your GitHub repository
377
+ as a deploy key to enable SSH-based git operations.
378
+ </p>
379
+
380
+ {!sshPublicKey ? (
381
+ <button
382
+ className={btnPrimary}
383
+ onClick={handleGenerateSshKey}
384
+ disabled={loading}
385
+ >
386
+ {loading ? 'Generating...' : 'Generate SSH Key'}
387
+ </button>
388
+ ) : (
389
+ <>
390
+ <label className="block text-content-secondary text-[11px] uppercase tracking-wider mb-1.5 mt-3">Public key:</label>
391
+ <div className="relative">
392
+ <textarea
393
+ className="w-full py-2 px-2.5 bg-surface-raised border border-edge rounded text-content font-mono text-[11px] outline-none resize-none h-16"
394
+ readOnly
395
+ value={sshPublicKey}
396
+ />
397
+ <button
398
+ className="absolute top-1.5 right-1.5 px-2 py-1 bg-surface-raised border border-edge rounded text-[10px] text-content-secondary hover:text-content cursor-pointer"
399
+ onClick={handleCopyPublicKey}
400
+ >
401
+ {copied ? 'Copied!' : 'Copy'}
402
+ </button>
403
+ </div>
404
+
405
+ <div className="mt-3 p-2.5 bg-[rgba(255,200,50,0.08)] border border-[rgba(255,200,50,0.2)] rounded text-xs text-content-secondary leading-relaxed">
406
+ Add this key as a <strong className="text-content">deploy key</strong> in your GitHub repo settings.
407
+ Make sure to check <strong className="text-content">&quot;Allow write access&quot;</strong> so the system can push commits.
408
+ </div>
409
+
410
+ <div className="mt-4 border-t border-edge pt-4">
411
+ <label className="block text-content-secondary text-[11px] uppercase tracking-wider mb-1.5">SSH clone URL:</label>
412
+ <input
413
+ className="w-full py-2 px-2.5 bg-surface-raised border border-edge rounded text-content font-mono text-xs outline-none focus:border-accent"
414
+ type="text"
415
+ placeholder="git@github.com:org/repo.git"
416
+ value={sshCloneUrl}
417
+ onChange={e => setSshCloneUrl(e.target.value)}
418
+ onKeyDown={e => { if (e.key === 'Enter') handleConnectSsh(); }}
419
+ />
420
+ <button
421
+ className={btnPrimary}
422
+ onClick={handleConnectSsh}
423
+ disabled={loading || !sshCloneUrl.trim()}
424
+ >
425
+ {loading ? 'Cloning...' : 'Clone & Connect via SSH'}
426
+ </button>
427
+ </div>
428
+
429
+ <button
430
+ className={`${btnBase} mt-4`}
431
+ onClick={handleGenerateSshKey}
432
+ disabled={loading}
433
+ >
434
+ {loading ? 'Regenerating...' : 'Regenerate Key'}
435
+ </button>
436
+ </>
437
+ )}
438
+ </div>
439
+ )}
440
+
310
441
  {tab === 'url' && (
311
- <div className="git-modal-url">
312
- <label className="git-modal-label">Git clone URL:</label>
442
+ <div>
443
+ <label className="block text-content-secondary text-[11px] uppercase tracking-wider mb-1.5 mt-3">Git clone URL:</label>
313
444
  <input
314
- className="git-modal-input"
445
+ className="w-full py-2 px-2.5 bg-surface-raised border border-edge rounded text-content font-mono text-xs outline-none focus:border-accent"
315
446
  type="text"
316
447
  placeholder="https://github.com/org/repo.git"
317
448
  value={cloneUrl}
@@ -319,7 +450,7 @@ export function GitRepoModal() {
319
450
  onKeyDown={e => { if (e.key === 'Enter') handleConnectUrl(); }}
320
451
  />
321
452
  <button
322
- className="git-modal-btn git-modal-btn-primary"
453
+ className={btnPrimary}
323
454
  onClick={handleConnectUrl}
324
455
  disabled={loading || !cloneUrl.trim()}
325
456
  >
@@ -329,13 +460,13 @@ export function GitRepoModal() {
329
460
  )}
330
461
 
331
462
  {tab === 'init' && (
332
- <div className="git-modal-init">
333
- <p className="git-modal-info">
463
+ <div>
464
+ <p className="text-content-secondary text-xs leading-relaxed mb-3">
334
465
  Initialize a new empty local git repository for this project.
335
466
  You can connect it to a remote later.
336
467
  </p>
337
468
  <button
338
- className="git-modal-btn git-modal-btn-primary"
469
+ className={btnPrimary}
339
470
  onClick={handleInit}
340
471
  disabled={loading}
341
472
  >
@@ -35,86 +35,84 @@ export function GraphLegend({ visible, onTypeToggle, onSearchChange }: GraphLege
35
35
  if (!visible) return null;
36
36
 
37
37
  return (
38
- <div id="graph-legend">
39
- <div className="graph-legend">
40
- <div className="legend-search-container" id="search-container">
41
- <input
42
- type="text"
43
- className="search-input"
44
- placeholder="Search nodes..."
45
- onChange={(e) => onSearchChange(e.target.value)}
46
- />
38
+ <div className="fixed bottom-4 left-4 bg-surface-alt border border-edge rounded-md z-[150] max-h-[calc(100vh-116px)] overflow-y-auto min-w-[180px] shadow-[0_2px_8px_var(--panel-shadow)]">
39
+ <div className="relative flex flex-wrap items-center gap-1.5 px-2.5 py-2 border-b border-edge" id="search-container">
40
+ <input
41
+ type="text"
42
+ className="flex-1 min-w-0 h-7 px-2 bg-surface-raised border border-edge rounded text-content font-mono text-[11px] outline-none transition-[border-color] duration-150 focus:border-accent placeholder:text-content-muted"
43
+ placeholder="Search nodes..."
44
+ onChange={(e) => onSearchChange(e.target.value)}
45
+ />
46
+ </div>
47
+ <div className="flex items-center gap-1.5 px-3 py-2 text-content-secondary font-mono text-[11px] font-semibold uppercase tracking-wide"><span>Legend</span></div>
48
+ <div className="px-3 pb-2.5">
49
+ <div className="mt-2 first:mt-0">
50
+ <div className="text-[10px] text-content-muted uppercase tracking-wider mb-1">Node Types</div>
51
+ <div className="flex flex-col gap-[3px]">
52
+ {Object.entries(NODE_COLORS).map(([type, color]) => {
53
+ const shape = NODE_SHAPES[type] || 'circle';
54
+ const pathD = nodeShapePath(shape, 6);
55
+ const active = !hiddenTypes.has(type);
56
+ return (
57
+ <div
58
+ key={type}
59
+ className={`flex items-center gap-2 cursor-pointer transition-opacity duration-150 rounded px-1 py-0.5 -mx-1 -my-0.5 hover:bg-surface-raised ${active ? 'opacity-100' : 'opacity-40'}`}
60
+ onClick={() => handleTypeClick(type)}
61
+ title={`Toggle ${LEGEND_LABELS[type]}`}
62
+ >
63
+ <svg className="shrink-0" width="16" height="16" viewBox="-8 -8 16 16">
64
+ <path d={pathD} fill={color} />
65
+ </svg>
66
+ <span className="text-[11px] text-content-secondary whitespace-nowrap">{LEGEND_LABELS[type] || type}</span>
67
+ </div>
68
+ );
69
+ })}
70
+ </div>
47
71
  </div>
48
- <div className="legend-header"><span>Legend</span></div>
49
- <div className="legend-body">
50
- <div className="legend-section">
51
- <div className="legend-section-title">Node Types</div>
52
- <div className="legend-items">
53
- {Object.entries(NODE_COLORS).map(([type, color]) => {
54
- const shape = NODE_SHAPES[type] || 'circle';
55
- const pathD = nodeShapePath(shape, 6);
56
- const active = !hiddenTypes.has(type);
57
- return (
58
- <div
59
- key={type}
60
- className={`legend-item legend-type-toggle${active ? ' active' : ''}`}
61
- onClick={() => handleTypeClick(type)}
62
- title={`Toggle ${LEGEND_LABELS[type]}`}
63
- >
64
- <svg className="legend-node-svg" width="16" height="16" viewBox="-8 -8 16 16">
65
- <path d={pathD} fill={color} />
66
- </svg>
67
- <span className="legend-item-label">{LEGEND_LABELS[type] || type}</span>
68
- </div>
69
- );
70
- })}
71
- </div>
72
+ <div className="mt-2 first:mt-0">
73
+ <div className="text-[10px] text-content-muted uppercase tracking-wider mb-1">Node Size</div>
74
+ <div className="flex items-center gap-1.5">
75
+ <span className="w-2 h-2 rounded-full" style={{ background: 'var(--text-muted)' }} />
76
+ <span className="text-[10px] text-content-muted">{'\u2192'}</span>
77
+ <span className="w-[18px] h-[18px] rounded-full" style={{ background: 'var(--text-muted)' }} />
78
+ <span className="text-[11px] text-content-secondary whitespace-nowrap">more connections = larger</span>
72
79
  </div>
73
- <div className="legend-section">
74
- <div className="legend-section-title">Node Size</div>
75
- <div className="legend-size-explanation">
76
- <span className="legend-size-small" />
77
- <span className="legend-size-arrow">{'\u2192'}</span>
78
- <span className="legend-size-large" />
79
- <span className="legend-item-label">more connections = larger</span>
80
- </div>
80
+ </div>
81
+ </div>
82
+ <button className="flex items-center gap-1.5 w-full px-3 py-1.5 bg-transparent border-none border-t border-edge text-content-secondary font-mono text-[11px] font-semibold cursor-pointer uppercase tracking-wide hover:text-content" onClick={toggleEdges} title="Toggle edge relations">
83
+ <span className="text-[9px]" dangerouslySetInnerHTML={{ __html: edgesCollapsed ? '&#9654;' : '&#9660;' }} />
84
+ <span>Edge Relations</span>
85
+ </button>
86
+ <div className={edgesCollapsed ? 'hidden' : 'px-3 pb-2.5'}>
87
+ <div className="mt-2 first:mt-0">
88
+ <div className="flex flex-col gap-[3px]">
89
+ {Object.entries(EDGE_COLORS).map(([relation, color]) => (
90
+ <div key={relation} className="flex items-center gap-2">
91
+ <span className="w-5 h-[3px] rounded-sm shrink-0" style={{ background: color }} />
92
+ <span className="text-[11px] text-content-secondary whitespace-nowrap">{EDGE_LABELS[relation] || relation}</span>
93
+ </div>
94
+ ))}
81
95
  </div>
82
96
  </div>
83
- <button className="legend-edges-toggle" onClick={toggleEdges} title="Toggle edge relations">
84
- <span className="legend-toggle-icon" dangerouslySetInnerHTML={{ __html: edgesCollapsed ? '&#9654;' : '&#9660;' }} />
85
- <span>Edge Relations</span>
86
- </button>
87
- <div className={`legend-edges-body${edgesCollapsed ? ' collapsed' : ''}`}>
88
- <div className="legend-section">
89
- <div className="legend-items">
90
- {Object.entries(EDGE_COLORS).map(([relation, color]) => (
91
- <div key={relation} className="legend-item">
92
- <span className="legend-edge-swatch" style={{ background: color }} />
93
- <span className="legend-item-label">{EDGE_LABELS[relation] || relation}</span>
94
- </div>
95
- ))}
97
+ <div className="mt-2 first:mt-0">
98
+ <div className="text-[10px] text-content-muted uppercase tracking-wider mb-1">Node Border</div>
99
+ <div className="flex flex-col gap-[3px]">
100
+ <div className="flex items-center gap-2">
101
+ <span className="w-3 h-3 rounded-full shrink-0 !bg-surface-raised border-2 border-[#ffd43b]" />
102
+ <span className="text-[11px] text-content-secondary whitespace-nowrap">incomplete</span>
96
103
  </div>
97
- </div>
98
- <div className="legend-section">
99
- <div className="legend-section-title">Node Border</div>
100
- <div className="legend-items">
101
- <div className="legend-item">
102
- <span className="legend-node-swatch legend-border-yellow" />
103
- <span className="legend-item-label">incomplete</span>
104
- </div>
105
- <div className="legend-item">
106
- <span className="legend-node-swatch legend-border-red" />
107
- <span className="legend-item-label">has open questions</span>
108
- </div>
104
+ <div className="flex items-center gap-2">
105
+ <span className="w-3 h-3 rounded-full shrink-0 !bg-surface-raised border-2 border-[#ff6b6b]" />
106
+ <span className="text-[11px] text-content-secondary whitespace-nowrap">has open questions</span>
109
107
  </div>
110
108
  </div>
111
- <div className="legend-section">
112
- <div className="legend-section-title">Gap Indicators</div>
113
- <div className="legend-items">
114
- <div className="legend-item">
115
- <span className="legend-node-swatch legend-gap-pulse" />
116
- <span className="legend-item-label">{'\u26A0'} pulsing = open questions</span>
117
- </div>
109
+ </div>
110
+ <div className="mt-2 first:mt-0">
111
+ <div className="text-[10px] text-content-muted uppercase tracking-wider mb-1">Gap Indicators</div>
112
+ <div className="flex flex-col gap-[3px]">
113
+ <div className="flex items-center gap-2">
114
+ <span className="w-3 h-3 rounded-full shrink-0 legend-gap-pulse" />
115
+ <span className="text-[11px] text-content-secondary whitespace-nowrap">{'\u26A0'} pulsing = open questions</span>
118
116
  </div>
119
117
  </div>
120
118
  </div>
@@ -89,18 +89,18 @@ export function GraphSettings({ isOpen, onClose, graphRef }: GraphSettingsProps)
89
89
  ];
90
90
 
91
91
  return (
92
- <div className={`settings-sheet${isOpen ? ' open' : ''}`} ref={sheetRef}>
93
- <div className="settings-header">
94
- <span className="settings-title">Settings</span>
95
- <button className="settings-close" onClick={onClose}>&times;</button>
92
+ <div className={`fixed top-[84px] right-0 w-[280px] bg-panel border-l border-edge shadow-[-4px_0_16px_var(--panel-shadow)] transition-transform duration-[250ms] ease-out z-[250] flex flex-col ${isOpen ? 'translate-x-0' : 'translate-x-full'}`} ref={sheetRef}>
93
+ <div className="flex items-center justify-between px-4 py-3 border-b border-edge min-h-12">
94
+ <span className="text-[13px] font-semibold text-content uppercase tracking-wide">Settings</span>
95
+ <button className="bg-transparent border-none text-content-secondary text-lg cursor-pointer px-2 py-1 rounded hover:bg-tab-hover hover:text-content" onClick={onClose}>&times;</button>
96
96
  </div>
97
- <div className="settings-body">
97
+ <div className="px-4 py-3 flex flex-col">
98
98
  {toggleItems.map(({ key, label }) => (
99
- <label key={key} className="settings-row">
100
- <span className="settings-label">{label}</span>
99
+ <label key={key} className="flex items-center justify-between py-2.5 border-b border-edge cursor-pointer last:border-b-0">
100
+ <span className="text-xs text-content-secondary">{label}</span>
101
101
  <input
102
102
  type="checkbox"
103
- className="settings-toggle"
103
+ className="settings-toggle appearance-none w-9 h-5 bg-surface-raised border border-edge rounded-[10px] relative cursor-pointer transition-[background,border-color] duration-150 shrink-0 checked:bg-[rgba(77,171,247,0.2)] checked:border-accent"
104
104
  checked={settings[key]}
105
105
  onChange={() => handleToggle(key)}
106
106
  />
@@ -365,6 +365,6 @@ export const GraphView = forwardRef<GraphViewHandle, GraphViewProps>(
365
365
  return () => window.removeEventListener('resize', handleResize);
366
366
  }, [visible]);
367
367
 
368
- return <div id="graph-container" ref={containerRef} style={{ display: visible ? 'block' : 'none' }} />;
368
+ return <div className="w-full h-[calc(100vh-44px)] overflow-hidden" ref={containerRef} style={{ display: visible ? 'block' : 'none' }} />;
369
369
  }
370
370
  );
@@ -50,22 +50,22 @@ export function InviteUserDialog({ isOpen, onClose }: InviteUserDialogProps) {
50
50
  if (!isOpen) return null;
51
51
 
52
52
  return (
53
- <div className="invite-dialog-overlay" onClick={handleClose}>
54
- <div className="invite-dialog" onClick={e => e.stopPropagation()}>
55
- <div className="invite-dialog-header">
56
- <h2>Invite User</h2>
57
- <button className="invite-dialog-close" onClick={handleClose} type="button">&times;</button>
53
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-[1000]" onClick={handleClose}>
54
+ <div className="bg-surface border border-edge rounded-xl p-6 w-[400px] max-w-[90vw] shadow-[0_8px_32px_rgba(0,0,0,0.3)]" onClick={e => e.stopPropagation()}>
55
+ <div className="flex justify-between items-center mb-4">
56
+ <h2 className="m-0 text-lg text-content">Invite User</h2>
57
+ <button className="bg-none border-none text-[22px] text-content-secondary cursor-pointer px-1 leading-none hover:text-content" onClick={handleClose} type="button">&times;</button>
58
58
  </div>
59
59
 
60
60
  <form onSubmit={handleSubmit} noValidate>
61
- {error && <div className="auth-error-banner">{error}</div>}
62
- {success && <div className="invite-dialog-success">{success}</div>}
61
+ {error && <div className="font-mono text-xs text-error bg-[rgba(255,107,107,0.08)] border border-[rgba(255,107,107,0.2)] rounded px-3 py-2 mb-3">{error}</div>}
62
+ {success && <div className="bg-surface-alt text-[#4caf50] px-3.5 py-2.5 rounded-md mb-3 text-sm">{success}</div>}
63
63
 
64
- <div className="auth-field">
65
- <label className="auth-label" htmlFor="invite-email">Email address</label>
64
+ <div className="flex flex-col gap-1">
65
+ <label className="font-mono text-[11px] font-semibold text-content-secondary uppercase tracking-wider" htmlFor="invite-email">Email address</label>
66
66
  <input
67
67
  id="invite-email"
68
- className={`auth-input${error ? ' auth-input-error' : ''}`}
68
+ className={`w-full h-9 px-2.5 bg-surface-raised border rounded text-content font-mono text-[13px] outline-none transition-[border-color] duration-150 focus:border-accent placeholder:text-content-muted ${error ? 'border-error focus:border-error' : 'border-edge'}`}
69
69
  type="email"
70
70
  value={email}
71
71
  onChange={e => setEmail(e.target.value)}
@@ -75,7 +75,11 @@ export function InviteUserDialog({ isOpen, onClose }: InviteUserDialogProps) {
75
75
  />
76
76
  </div>
77
77
 
78
- <button className="auth-submit" type="submit" disabled={submitting}>
78
+ <button
79
+ className="w-full h-9 mt-4 bg-transparent border border-accent rounded text-accent font-mono text-[13px] cursor-pointer transition-[background,color] duration-150 hover:enabled:bg-accent hover:enabled:text-white disabled:opacity-60 disabled:cursor-not-allowed"
80
+ type="submit"
81
+ disabled={submitting}
82
+ >
79
83
  {submitting ? 'Sending...' : 'Send Invitation'}
80
84
  </button>
81
85
  </form>