@assistkick/create 1.7.0 → 1.9.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 (206) 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 +61 -6
  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 +158 -0
  19. package/templates/assistkick-product-system/packages/backend/src/server.ts +60 -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 +43 -77
  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 +245 -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 +458 -18
  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/IterationCommentModal.tsx +80 -0
  62. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +263 -167
  63. package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
  64. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
  65. package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +32 -49
  66. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +43 -48
  67. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
  68. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +20 -14
  69. package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
  70. package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
  71. package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +103 -87
  82. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +9 -188
  83. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
  85. package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +81 -37
  86. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
  87. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
  88. package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
  89. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
  92. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GenerateTTSNode.tsx +52 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RebuildBundleNode.tsx +20 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RenderVideoNode.tsx +72 -0
  97. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
  99. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +341 -0
  102. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +643 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
  105. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +246 -0
  106. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
  107. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +136 -0
  108. package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
  109. package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
  110. package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
  111. package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
  112. package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
  113. package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
  114. package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
  115. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +1 -1
  117. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +2 -2
  118. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -2
  120. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +6 -3
  123. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +4 -4
  124. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +275 -3535
  125. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
  131. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
  132. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
  133. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +5 -0
  134. package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
  135. package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
  136. package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
  137. package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
  138. package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
  139. package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
  140. package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
  141. package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
  142. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
  143. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
  144. package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
  145. package/templates/assistkick-product-system/packages/shared/db/migrations/0014_nifty_punisher.sql +15 -0
  146. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
  147. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
  148. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
  149. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
  150. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
  151. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
  152. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
  153. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
  154. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
  155. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0014_snapshot.json +1545 -0
  156. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +77 -0
  157. package/templates/assistkick-product-system/packages/shared/db/schema.ts +114 -0
  158. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
  159. package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
  160. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
  161. package/templates/assistkick-product-system/packages/shared/lib/graph.ts +5 -0
  162. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1999 -0
  163. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1437 -0
  164. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
  165. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
  166. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
  167. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
  168. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
  169. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
  170. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  171. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
  172. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
  173. package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
  174. package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
  175. package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
  176. package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
  177. package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
  178. package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
  179. package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
  180. package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
  181. package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
  182. package/templates/assistkick-product-system/packages/video/index.ts +4 -0
  183. package/templates/assistkick-product-system/packages/video/package.json +28 -0
  184. package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
  185. package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
  186. package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
  187. package/templates/assistkick-product-system/packages/video/style.css +1 -0
  188. package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
  189. package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
  190. package/templates/assistkick-product-system/tests/video_render_service.test.ts +181 -0
  191. package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
  192. package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
  193. package/templates/skills/assistkick-developer/SKILL.md +3 -0
  194. package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
  195. package/templates/skills/product-system/graph.json +1890 -0
  196. package/templates/skills/product-system/kanban.json +304 -0
  197. package/templates/skills/product-system/nodes/comp_001.md +56 -0
  198. package/templates/skills/product-system/nodes/comp_002.md +57 -0
  199. package/templates/skills/product-system/nodes/data_001.md +51 -0
  200. package/templates/skills/product-system/nodes/data_002.md +40 -0
  201. package/templates/skills/product-system/nodes/data_004.md +38 -0
  202. package/templates/skills/product-system/nodes/dec_001.md +34 -0
  203. package/templates/skills/product-system/nodes/dec_016.md +32 -0
  204. package/templates/skills/product-system/nodes/feat_008.md +30 -0
  205. package/templates/skills/video-composition-agent/SKILL.md +232 -0
  206. package/templates/skills/video-script-writer/SKILL.md +136 -0
@@ -2,6 +2,7 @@
2
2
  * Terminal view — multi-session manager with chat-like layout.
3
3
  * Left sidebar lists all terminal sessions; right panel shows the active session's terminal.
4
4
  * Each session is permanently bound to a project chosen at creation time.
5
+ * Sessions persist across server restarts — suspended sessions are auto-resumed on connect.
5
6
  * Admin-only access.
6
7
  */
7
8
 
@@ -12,6 +13,10 @@ import { WebLinksAddon } from '@xterm/addon-web-links';
12
13
  import '@xterm/xterm/css/xterm.css';
13
14
  import { apiClient } from '../api/client';
14
15
  import type { Project } from '../hooks/useProjects';
16
+ import { useProjectStore } from '../stores/useProjectStore';
17
+ import { Button } from './ds/Button';
18
+ import { IconButton } from './ds/IconButton';
19
+ import { X, Plus, RefreshCw } from 'lucide-react';
15
20
 
16
21
  interface TerminalViewProps {
17
22
  visible: boolean;
@@ -20,11 +25,13 @@ interface TerminalViewProps {
20
25
 
21
26
  interface SessionInfo {
22
27
  id: string;
28
+ claudeSessionId: string;
23
29
  name: string;
24
30
  projectId: string;
25
31
  projectName: string;
26
- state: 'idle' | 'running';
32
+ state: 'suspended' | 'running';
27
33
  createdAt: string;
34
+ lastUsedAt: string;
28
35
  }
29
36
 
30
37
  type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
@@ -43,15 +50,11 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
43
50
  const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
44
51
  const [status, setStatus] = useState<ConnectionStatus>('disconnected');
45
52
  const [errorMsg, setErrorMsg] = useState('');
46
- const [newSessionProjectId, setNewSessionProjectId] = useState<string>('');
47
53
  const [creating, setCreating] = useState(false);
54
+ const [isDragging, setIsDragging] = useState(false);
55
+ const dragCounterRef = useRef(0);
48
56
 
49
- // Default new session project picker to first project
50
- useEffect(() => {
51
- if (projects.length > 0 && !newSessionProjectId) {
52
- setNewSessionProjectId(projects[0].id);
53
- }
54
- }, [projects, newSessionProjectId]);
57
+ const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
55
58
 
56
59
  const fetchSessions = useCallback(async () => {
57
60
  try {
@@ -120,8 +123,10 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
120
123
  terminal.open(containerRef.current);
121
124
  fitAddon.fit();
122
125
 
126
+ // Pass terminal dimensions so the server can resume with correct size
127
+ const { cols, rows } = terminal;
123
128
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
124
- const wsUrl = `${protocol}//${window.location.host}/api/terminal?sessionId=${encodeURIComponent(sessionId)}`;
129
+ const wsUrl = `${protocol}//${window.location.host}/api/terminal?sessionId=${encodeURIComponent(sessionId)}&cols=${cols}&rows=${rows}`;
125
130
 
126
131
  setStatus('connecting');
127
132
  setErrorMsg('');
@@ -132,8 +137,10 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
132
137
  ws.onopen = () => {
133
138
  setStatus('connected');
134
139
  fitAddon.fit();
135
- const { cols, rows } = terminal;
136
- ws.send(JSON.stringify({ type: 'resize', cols, rows }));
140
+ const dims = terminal;
141
+ ws.send(JSON.stringify({ type: 'resize', cols: dims.cols, rows: dims.rows }));
142
+ // Refresh sessions to update state from suspended → running
143
+ fetchSessions();
137
144
  };
138
145
 
139
146
  ws.onmessage = (event) => {
@@ -159,7 +166,7 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
159
166
  setErrorMsg('Access denied. Admin privileges required.');
160
167
  } else if (event.code === 4004) {
161
168
  setStatus('error');
162
- setErrorMsg('Session not found. It may have been killed.');
169
+ setErrorMsg('Session not found. It may have been deleted.');
163
170
  fetchSessions();
164
171
  } else {
165
172
  setStatus('disconnected');
@@ -188,7 +195,7 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
188
195
  }, [connectToSession]);
189
196
 
190
197
  const handleCreateSession = useCallback(async () => {
191
- const projectId = newSessionProjectId || projects[0]?.id;
198
+ const projectId = selectedProjectId || projects[0]?.id;
192
199
  if (!projectId) return;
193
200
 
194
201
  const project = projects.find(p => p.id === projectId) || projects[0];
@@ -207,7 +214,7 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
207
214
  } finally {
208
215
  setCreating(false);
209
216
  }
210
- }, [newSessionProjectId, projects, fetchSessions, connectToSession]);
217
+ }, [selectedProjectId, projects, fetchSessions, connectToSession]);
211
218
 
212
219
  const handleKillSession = useCallback(async (sessionId: string, e: React.MouseEvent) => {
213
220
  e.stopPropagation();
@@ -249,82 +256,144 @@ export function TerminalView({ visible, projects }: TerminalViewProps) {
249
256
  };
250
257
  }, [disconnectCurrent]);
251
258
 
259
+ // --- Image drag-and-drop handlers ---
260
+ const handleDragEnter = useCallback((e: React.DragEvent) => {
261
+ e.preventDefault();
262
+ e.stopPropagation();
263
+ dragCounterRef.current++;
264
+ if (e.dataTransfer.types.includes('Files')) {
265
+ setIsDragging(true);
266
+ }
267
+ }, []);
268
+
269
+ const handleDragLeave = useCallback((e: React.DragEvent) => {
270
+ e.preventDefault();
271
+ e.stopPropagation();
272
+ dragCounterRef.current--;
273
+ if (dragCounterRef.current === 0) {
274
+ setIsDragging(false);
275
+ }
276
+ }, []);
277
+
278
+ const handleDragOver = useCallback((e: React.DragEvent) => {
279
+ e.preventDefault();
280
+ e.stopPropagation();
281
+ }, []);
282
+
283
+ const handleDrop = useCallback(async (e: React.DragEvent) => {
284
+ e.preventDefault();
285
+ e.stopPropagation();
286
+ dragCounterRef.current = 0;
287
+ setIsDragging(false);
288
+
289
+ const conn = connectionRef.current;
290
+ if (!conn || conn.ws.readyState !== WebSocket.OPEN) return;
291
+
292
+ const files = Array.from(e.dataTransfer.files);
293
+ const imageFiles = files.filter(f => f.type.startsWith('image/'));
294
+ if (imageFiles.length === 0) return;
295
+
296
+ for (const file of imageFiles) {
297
+ try {
298
+ const { path } = await apiClient.uploadTerminalImage(file);
299
+ conn.ws.send(JSON.stringify({ type: 'input', data: path + ' ' }));
300
+ } catch (err) {
301
+ console.error('Failed to upload image:', err);
302
+ }
303
+ }
304
+ }, []);
305
+
252
306
  const activeSession = sessions.find(s => s.id === activeSessionId);
253
307
 
254
308
  return (
255
- <div className="terminal-view" style={{ display: visible ? 'flex' : 'none' }}>
309
+ <div className="flex flex-row h-full w-full overflow-hidden" style={{ display: visible ? 'flex' : 'none' }}>
256
310
  {/* Left sidebar — session list */}
257
- <div className="terminal-sidebar">
258
- <div className="terminal-sidebar-header">Sessions</div>
259
- <div className="terminal-session-list">
311
+ <div className="flex flex-col w-[280px] min-w-[280px] border-r border-edge bg-surface-alt overflow-hidden">
312
+ <div className="px-3 py-2.5 text-[11px] font-mono uppercase tracking-widest text-content-muted border-b border-edge shrink-0">Sessions</div>
313
+ <div className="flex-1 overflow-y-auto">
260
314
  {sessions.length === 0 && (
261
- <div className="terminal-no-sessions">No sessions yet</div>
315
+ <div className="px-3 py-4 text-xs text-content-muted font-mono">No sessions yet</div>
262
316
  )}
263
317
  {sessions.map(session => (
264
318
  <div
265
319
  key={session.id}
266
- className={`terminal-session-item${session.id === activeSessionId ? ' terminal-session-item--active' : ''}`}
320
+ className={`flex flex-col px-2.5 py-2 cursor-pointer border-b border-edge relative hover:bg-tab-hover${session.id === activeSessionId ? ' bg-tab-active border-l-2 border-l-accent pl-2' : ''}`}
267
321
  onClick={() => handleSelectSession(session.id)}
268
322
  >
269
- <div className="terminal-session-name">{session.name}</div>
270
- <div className="terminal-session-project">{session.projectName}</div>
271
- <button
272
- className="terminal-session-kill"
323
+ <div className="text-xs font-mono text-content whitespace-nowrap overflow-hidden text-ellipsis pr-5 flex items-center gap-1">
324
+ {session.state === 'suspended' && (
325
+ <span className="shrink-0 text-[10px] leading-none text-content-muted opacity-70" title="Suspended — will resume on connect">&#x23F8;</span>
326
+ )}
327
+ {session.name}
328
+ </div>
329
+ <div className="text-[11px] text-content-muted font-mono whitespace-nowrap overflow-hidden text-ellipsis pr-5 mt-0.5">{session.projectName}</div>
330
+ <IconButton
331
+ label="Kill session"
332
+ variant="danger"
333
+ size="sm"
334
+ className="absolute top-2 right-2"
273
335
  onClick={(e) => handleKillSession(session.id, e)}
274
- title="Kill session"
275
336
  >
276
-
277
- </button>
337
+ <X size={10} strokeWidth={2} />
338
+ </IconButton>
278
339
  </div>
279
340
  ))}
280
341
  </div>
281
342
 
282
- {/* New session controls */}
283
- <div className="terminal-new-session">
284
- <select
285
- className="terminal-project-picker"
286
- value={newSessionProjectId}
287
- onChange={e => setNewSessionProjectId(e.target.value)}
288
- disabled={creating || projects.length === 0}
289
- >
290
- {projects.map(p => (
291
- <option key={p.id} value={p.id}>{p.name}</option>
292
- ))}
293
- </select>
294
- <button
295
- className="terminal-new-session-btn"
343
+ {/* New session uses the project selected in the navbar */}
344
+ <div className="flex flex-col gap-1.5 p-2.5 border-t border-edge shrink-0">
345
+ <Button
346
+ variant="primary"
347
+ size="sm"
348
+ icon={!creating ? <Plus size={12} strokeWidth={2} /> : undefined}
349
+ className="w-full"
296
350
  onClick={handleCreateSession}
297
- disabled={creating || projects.length === 0}
351
+ disabled={creating || !selectedProjectId}
298
352
  >
299
- {creating ? '' : '+ New'}
300
- </button>
353
+ {creating ? '...' : 'New'}
354
+ </Button>
301
355
  </div>
302
356
  </div>
303
357
 
304
358
  {/* Right panel — terminal content */}
305
- <div className="terminal-panel">
359
+ <div
360
+ className="flex-1 flex flex-col min-w-0 relative"
361
+ onDragEnter={handleDragEnter}
362
+ onDragLeave={handleDragLeave}
363
+ onDragOver={handleDragOver}
364
+ onDrop={handleDrop}
365
+ >
366
+ {isDragging && (
367
+ <div className="absolute inset-0 z-10 flex items-center justify-center bg-[rgba(30,30,46,0.85)] border-2 border-dashed border-[#89b4fa] rounded-lg pointer-events-none">
368
+ <div className="text-[#89b4fa] text-lg font-mono font-semibold">Drop image here</div>
369
+ </div>
370
+ )}
306
371
  {!activeSessionId && (
307
- <div className="terminal-empty-state">
372
+ <div className="flex-1 flex items-center justify-center text-content-muted text-[13px] font-mono">
308
373
  Select a session or create a new one
309
374
  </div>
310
375
  )}
311
376
  {activeSession && errorMsg && (
312
- <div className="terminal-error">
377
+ <div className="flex items-center gap-3 px-4 py-2 bg-error text-[#1e1e2e] text-[13px] font-mono shrink-0">
313
378
  <span>{errorMsg}</span>
314
- <button
315
- className="terminal-reconnect-btn"
379
+ <Button
380
+ variant="secondary"
381
+ size="sm"
382
+ icon={<RefreshCw size={12} strokeWidth={2} />}
316
383
  onClick={() => connectToSession(activeSessionId!)}
317
384
  >
318
385
  Reconnect
319
- </button>
386
+ </Button>
320
387
  </div>
321
388
  )}
322
389
  {activeSession && status === 'connecting' && (
323
- <div className="terminal-status">Connecting…</div>
390
+ <div className="px-4 py-2 text-content-secondary text-[13px] font-mono shrink-0">
391
+ {activeSession.state === 'suspended' ? 'Resuming session...' : 'Connecting...'}
392
+ </div>
324
393
  )}
325
394
  <div
326
395
  ref={containerRef}
327
- className="terminal-container"
396
+ className="terminal-container flex-1 p-1 bg-[#1e1e2e] min-h-0"
328
397
  style={{ display: activeSessionId ? 'flex' : 'none' }}
329
398
  />
330
399
  </div>
@@ -1,8 +1,8 @@
1
1
  import React, { useCallback, useEffect } from 'react';
2
2
  import { useLocation, useNavigate } from 'react-router-dom';
3
3
  import {
4
- Columns3, MessageSquare, Network, ShieldCheck, Palette, Users,
5
- Maximize, Settings, Sun, Moon,
4
+ Columns3, MessageSquare, Network, ShieldCheck, Palette, Users, Bot, Workflow,
5
+ FolderOpen, Maximize, Settings, Sun, Moon, Video,
6
6
  } from 'lucide-react';
7
7
  import { NavBarSidekick } from './ds/NavBarSidekick';
8
8
  import type { NavItem } from './ds/NavBarSidekick';
@@ -15,7 +15,7 @@ import { useTheme } from '../hooks/useTheme';
15
15
  import { useAuth } from '../hooks/useAuth';
16
16
  import { apiClient } from '../api/client';
17
17
 
18
- const VALID_TABS = new Set(['graph', 'kanban', 'coherence', 'users', 'terminal', 'design-system']);
18
+ const VALID_TABS = new Set(['graph', 'kanban', 'coherence', 'users', 'terminal', 'agents', 'workflows', 'design-system', 'files', 'videography']);
19
19
 
20
20
  function tabFromPath(pathname: string): string {
21
21
  const seg = pathname.replace(/^\//, '');
@@ -25,14 +25,18 @@ function tabFromPath(pathname: string): string {
25
25
  const ICON_PROPS = { size: 14, strokeWidth: 2 } as const;
26
26
 
27
27
  const TAB_ITEMS: NavItem[] = [
28
- { id: 'kanban', label: 'Kanban', icon: <Columns3 {...ICON_PROPS} />, description: 'Board view of features' },
29
- { id: 'terminal', label: 'Chat', icon: <MessageSquare {...ICON_PROPS} />, description: 'AI assistant terminal' },
30
- { id: 'graph', label: 'Graph', icon: <Network {...ICON_PROPS} />, description: 'Knowledge graph visualization' },
31
- { id: 'coherence', label: 'Coherence', icon: <ShieldCheck {...ICON_PROPS} />, description: 'Coherence analysis' },
32
- { id: 'design-system', label: 'Design System', icon: <Palette {...ICON_PROPS} />, description: 'Living style guide' },
28
+ { id: 'kanban', label: 'Kanban', icon: <Columns3 {...ICON_PROPS} />, description: 'Board view of features', href: '/kanban' },
29
+ { id: 'terminal', label: 'Chat', icon: <MessageSquare {...ICON_PROPS} />, description: 'AI assistant terminal', href: '/terminal' },
30
+ { id: 'graph', label: 'Graph', icon: <Network {...ICON_PROPS} />, description: 'Knowledge graph visualization', href: '/graph' },
31
+ { id: 'coherence', label: 'Coherence', icon: <ShieldCheck {...ICON_PROPS} />, description: 'Coherence analysis', href: '/coherence' },
32
+ { id: 'agents', label: 'Agents', icon: <Bot {...ICON_PROPS} />, description: 'Manage agent prompts', href: '/agents' },
33
+ { id: 'workflows', label: 'Workflows', icon: <Workflow {...ICON_PROPS} />, description: 'Visual workflow builder', href: '/workflows' },
34
+ { id: 'files', label: 'Files', icon: <FolderOpen {...ICON_PROPS} />, description: 'Browse and edit project files', href: '/files' },
35
+ { id: 'design-system', label: 'Design System', icon: <Palette {...ICON_PROPS} />, description: 'Living style guide', href: '/design-system' },
33
36
  ];
34
37
 
35
- const ADMIN_ITEM: NavItem = { id: 'users', label: 'Users', icon: <Users {...ICON_PROPS} />, description: 'User management' };
38
+ const VIDEOGRAPHY_ITEM: NavItem = { id: 'videography', label: 'Videography', icon: <Video {...ICON_PROPS} />, description: 'Video production pipeline', href: '/videography' };
39
+ const ADMIN_ITEM: NavItem = { id: 'users', label: 'Users', icon: <Users {...ICON_PROPS} />, description: 'User management', href: '/users' };
36
40
 
37
41
  const iconBtnClass = (active?: boolean) => [
38
42
  'flex h-8 w-8 items-center justify-center rounded-lg border',
@@ -69,11 +73,14 @@ export function Toolbar() {
69
73
  ? Math.round((graphData.nodes.reduce((acc: number, n: any) => acc + (n.completeness || 0), 0) / Math.max(graphData.nodes.length, 1)) * 100)
70
74
  : 0;
71
75
 
72
- const items = isAdmin ? [...TAB_ITEMS, ADMIN_ITEM] : TAB_ITEMS;
76
+ const selectedProject = projects.find(p => p.id === selectedProjectId);
77
+ const isVideoProject = selectedProject?.type === 'video';
73
78
 
74
- const handleNavigate = useCallback((id: string) => {
75
- navigate(`/${id}`);
76
- }, [navigate]);
79
+ const items = [
80
+ ...TAB_ITEMS,
81
+ ...(isVideoProject ? [VIDEOGRAPHY_ITEM] : []),
82
+ ...(isAdmin ? [ADMIN_ITEM] : []),
83
+ ];
77
84
 
78
85
  const handleLogout = useCallback(async () => {
79
86
  try {
@@ -103,7 +110,6 @@ export function Toolbar() {
103
110
  <NavBarSidekick
104
111
  items={items}
105
112
  activeId={activeTab}
106
- onNavigate={handleNavigate}
107
113
  brand={
108
114
  <ProjectSelector
109
115
  projects={projects}
@@ -110,43 +110,43 @@ export function UsersView({ visible }: UsersViewProps) {
110
110
  if (!visible) return null;
111
111
 
112
112
  return (
113
- <div className="users-view">
114
- <div className="users-view-header">
115
- <h2>User Management</h2>
116
- <button className="users-invite-btn" onClick={() => setInviteOpen(true)}>
113
+ <div className="p-6 max-w-[900px] mx-auto text-content">
114
+ <div className="flex justify-between items-center mb-5">
115
+ <h2 className="text-xl font-semibold">User Management</h2>
116
+ <button className="bg-accent text-white border-none rounded-md px-4 py-2 text-sm cursor-pointer font-mono hover:opacity-90" onClick={() => setInviteOpen(true)}>
117
117
  + Invite User
118
118
  </button>
119
119
  </div>
120
120
 
121
- {error && <div className="users-error">{error}<button className="users-error-dismiss" onClick={() => setError('')}>&times;</button></div>}
121
+ {error && <div className="bg-[rgba(255,107,107,0.15)] text-error px-3.5 py-2.5 rounded-md mb-4 text-sm flex justify-between items-center">{error}<button className="bg-none border-none text-error text-lg cursor-pointer px-1" onClick={() => setError('')}>&times;</button></div>}
122
122
 
123
123
  {loading ? (
124
- <div className="users-loading">Loading...</div>
124
+ <div className="text-content-secondary py-10 text-center">Loading...</div>
125
125
  ) : (
126
126
  <>
127
- <section className="users-section">
128
- <h3>Registered Users ({users.length})</h3>
129
- <table className="users-table">
127
+ <section className="mb-7">
128
+ <h3 className="text-[15px] font-semibold text-content-secondary mb-2.5">Registered Users ({users.length})</h3>
129
+ <table className="w-full border-collapse text-sm font-mono">
130
130
  <thead>
131
131
  <tr>
132
- <th>Email</th>
133
- <th>Role</th>
134
- <th>Created</th>
135
- <th></th>
132
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Email</th>
133
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Role</th>
134
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Created</th>
135
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide"></th>
136
136
  </tr>
137
137
  </thead>
138
138
  <tbody>
139
139
  {users.map(u => (
140
- <tr key={u.id} className={u.id === currentUser?.id ? 'users-current-row' : ''}>
141
- <td>{u.email}</td>
142
- <td><span className={`users-role-badge users-role-${u.role}`}>{u.role}</span></td>
143
- <td>{formatDate(u.createdAt)}</td>
144
- <td>
140
+ <tr key={u.id} className={`hover:bg-surface-raised ${u.id === currentUser?.id ? 'bg-surface-alt' : ''}`}>
141
+ <td className="px-3 py-2.5 border-b border-edge text-content">{u.email}</td>
142
+ <td className="px-3 py-2.5 border-b border-edge text-content"><span className={`inline-block px-2 py-0.5 rounded text-xs font-medium ${u.role === 'admin' ? 'bg-[rgba(77,171,247,0.15)] text-accent' : 'bg-surface-raised text-content-secondary'}`}>{u.role}</span></td>
143
+ <td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(u.createdAt)}</td>
144
+ <td className="px-3 py-2.5 border-b border-edge text-content">
145
145
  {u.id === currentUser?.id ? (
146
- <span className="users-you-badge">you</span>
146
+ <span className="text-xs text-content-muted italic">you</span>
147
147
  ) : (
148
148
  <button
149
- className="users-delete-btn"
149
+ className="bg-none border border-edge text-error rounded px-2.5 py-1 text-xs cursor-pointer font-mono hover:bg-[rgba(255,107,107,0.1)] hover:border-error disabled:opacity-50 disabled:cursor-not-allowed"
150
150
  onClick={() => handleDeleteUser(u.id, u.email)}
151
151
  disabled={deletingId === u.id}
152
152
  title={`Delete ${u.email}`}
@@ -161,31 +161,31 @@ export function UsersView({ visible }: UsersViewProps) {
161
161
  </table>
162
162
  </section>
163
163
 
164
- <section className="users-section">
165
- <h3>Pending Invitations ({pendingInvitations.length})</h3>
164
+ <section className="mb-7">
165
+ <h3 className="text-[15px] font-semibold text-content-secondary mb-2.5">Pending Invitations ({pendingInvitations.length})</h3>
166
166
  {pendingInvitations.length === 0 ? (
167
- <p className="users-empty">No pending invitations.</p>
167
+ <p className="text-content-muted text-sm py-3">No pending invitations.</p>
168
168
  ) : (
169
- <table className="users-table">
169
+ <table className="w-full border-collapse text-sm font-mono">
170
170
  <thead>
171
171
  <tr>
172
- <th>Email</th>
173
- <th>Invited By</th>
174
- <th>Sent</th>
175
- <th>Expires</th>
176
- <th></th>
172
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Email</th>
173
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Invited By</th>
174
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Sent</th>
175
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Expires</th>
176
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide"></th>
177
177
  </tr>
178
178
  </thead>
179
179
  <tbody>
180
180
  {pendingInvitations.map(inv => (
181
- <tr key={inv.id}>
182
- <td>{inv.email}</td>
183
- <td>{inv.invitedByEmail}</td>
184
- <td>{formatDate(inv.createdAt)}</td>
185
- <td>{formatDate(inv.expiresAt)}</td>
186
- <td>
181
+ <tr key={inv.id} className="hover:bg-surface-raised">
182
+ <td className="px-3 py-2.5 border-b border-edge text-content">{inv.email}</td>
183
+ <td className="px-3 py-2.5 border-b border-edge text-content">{inv.invitedByEmail}</td>
184
+ <td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(inv.createdAt)}</td>
185
+ <td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(inv.expiresAt)}</td>
186
+ <td className="px-3 py-2.5 border-b border-edge text-content">
187
187
  <button
188
- className="users-delete-btn"
188
+ className="bg-none border border-edge text-error rounded px-2.5 py-1 text-xs cursor-pointer font-mono hover:bg-[rgba(255,107,107,0.1)] hover:border-error disabled:opacity-50 disabled:cursor-not-allowed"
189
189
  onClick={() => handleDeleteInvitation(inv.id, inv.email)}
190
190
  disabled={deletingId === inv.id}
191
191
  title={`Cancel invitation for ${inv.email}`}
@@ -201,32 +201,32 @@ export function UsersView({ visible }: UsersViewProps) {
201
201
  </section>
202
202
 
203
203
  {pastInvitations.length > 0 && (
204
- <section className="users-section">
205
- <h3>Past Invitations ({pastInvitations.length})</h3>
206
- <table className="users-table">
204
+ <section className="mb-7">
205
+ <h3 className="text-[15px] font-semibold text-content-secondary mb-2.5">Past Invitations ({pastInvitations.length})</h3>
206
+ <table className="w-full border-collapse text-sm font-mono">
207
207
  <thead>
208
208
  <tr>
209
- <th>Email</th>
210
- <th>Invited By</th>
211
- <th>Status</th>
212
- <th>Date</th>
213
- <th></th>
209
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Email</th>
210
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Invited By</th>
211
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Status</th>
212
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide">Date</th>
213
+ <th className="text-left px-3 py-2 border-b border-edge text-content-muted font-medium text-xs uppercase tracking-wide"></th>
214
214
  </tr>
215
215
  </thead>
216
216
  <tbody>
217
217
  {pastInvitations.map(inv => (
218
- <tr key={inv.id} className="users-past-row">
219
- <td>{inv.email}</td>
220
- <td>{inv.invitedByEmail}</td>
221
- <td>
222
- <span className={`users-status-badge ${inv.acceptedAt ? 'users-status-accepted' : 'users-status-expired'}`}>
218
+ <tr key={inv.id} className="opacity-70 hover:bg-surface-raised">
219
+ <td className="px-3 py-2.5 border-b border-edge text-content">{inv.email}</td>
220
+ <td className="px-3 py-2.5 border-b border-edge text-content">{inv.invitedByEmail}</td>
221
+ <td className="px-3 py-2.5 border-b border-edge text-content">
222
+ <span className={`inline-block px-2 py-0.5 rounded text-xs font-medium ${inv.acceptedAt ? 'bg-[rgba(105,219,124,0.15)] text-completeness-fill' : 'bg-[rgba(255,107,107,0.15)] text-error'}`}>
223
223
  {inv.acceptedAt ? 'Accepted' : 'Expired'}
224
224
  </span>
225
225
  </td>
226
- <td>{formatDate(inv.acceptedAt || inv.expiresAt)}</td>
227
- <td>
226
+ <td className="px-3 py-2.5 border-b border-edge text-content">{formatDate(inv.acceptedAt || inv.expiresAt)}</td>
227
+ <td className="px-3 py-2.5 border-b border-edge text-content">
228
228
  <button
229
- className="users-delete-btn"
229
+ className="bg-none border border-edge text-error rounded px-2.5 py-1 text-xs cursor-pointer font-mono hover:bg-[rgba(255,107,107,0.1)] hover:border-error disabled:opacity-50 disabled:cursor-not-allowed"
230
230
  onClick={() => handleDeleteInvitation(inv.id, inv.email)}
231
231
  disabled={deletingId === inv.id}
232
232
  title={`Delete invitation record for ${inv.email}`}