@assistkick/create 1.6.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 (214) hide show
  1. package/package.json +2 -2
  2. package/templates/assistkick-product-system/.env.example +1 -0
  3. package/templates/assistkick-product-system/local.db +0 -0
  4. package/templates/assistkick-product-system/package.json +4 -2
  5. package/templates/assistkick-product-system/packages/backend/package.json +2 -0
  6. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
  7. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
  9. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
  10. package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
  11. package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +43 -4
  12. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
  13. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
  14. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
  15. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
  16. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
  17. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +154 -0
  18. package/templates/assistkick-product-system/packages/backend/src/server.ts +81 -9
  19. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
  20. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
  21. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
  22. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
  23. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +28 -78
  24. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
  25. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
  26. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
  27. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
  28. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
  29. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
  30. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
  31. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
  32. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
  33. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
  34. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
  35. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +222 -0
  40. package/templates/assistkick-product-system/packages/frontend/index.html +3 -0
  41. package/templates/assistkick-product-system/packages/frontend/package-lock.json +800 -11
  42. package/templates/assistkick-product-system/packages/frontend/package.json +11 -1
  43. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +24 -7
  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 +383 -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 +193 -64
  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 +226 -291
  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 +40 -66
  65. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +55 -115
  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 +155 -77
  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 +270 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +37 -0
  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 +207 -0
  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/useGraph.ts +6 -21
  107. package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +15 -80
  108. package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
  109. package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
  110. package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
  111. package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
  112. package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
  113. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
  114. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +19 -0
  115. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +54 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +6 -0
  117. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
  118. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +93 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +30 -0
  120. package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +9 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +6 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
  123. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
  124. package/templates/assistkick-product-system/packages/frontend/src/stores/useGitModalStore.ts +14 -0
  125. package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphStore.ts +36 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphUIStore.ts +25 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +90 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/stores/useQaSheetStore.ts +27 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +76 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +336 -3632
  131. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
  132. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
  133. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
  134. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
  135. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
  136. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
  137. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
  138. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
  139. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +7 -1
  140. package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
  141. package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
  142. package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
  143. package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
  144. package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
  145. package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
  146. package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
  147. package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
  148. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
  149. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
  150. package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
  151. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
  152. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
  153. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
  154. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
  155. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
  156. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
  157. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
  158. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
  159. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
  160. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
  161. package/templates/assistkick-product-system/packages/shared/db/schema.ts +113 -0
  162. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
  163. package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
  164. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
  165. package/templates/assistkick-product-system/packages/shared/lib/graph.ts +16 -5
  166. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1753 -0
  167. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1281 -0
  168. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
  169. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
  170. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
  171. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
  172. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
  173. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
  174. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  175. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
  176. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
  177. package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
  178. package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
  179. package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
  180. package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
  181. package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
  182. package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
  183. package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
  184. package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
  185. package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
  186. package/templates/assistkick-product-system/packages/video/index.ts +4 -0
  187. package/templates/assistkick-product-system/packages/video/package.json +28 -0
  188. package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
  189. package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
  190. package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
  191. package/templates/assistkick-product-system/packages/video/style.css +1 -0
  192. package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
  193. package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
  194. package/templates/assistkick-product-system/tests/video_render_service.test.ts +179 -0
  195. package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
  196. package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
  197. package/templates/skills/assistkick-bootstrap/SKILL.md +3 -3
  198. package/templates/skills/assistkick-code-reviewer/SKILL.md +2 -2
  199. package/templates/skills/assistkick-debugger/SKILL.md +2 -2
  200. package/templates/skills/assistkick-developer/SKILL.md +6 -3
  201. package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
  202. package/templates/skills/assistkick-interview/SKILL.md +2 -2
  203. package/templates/skills/product-system/graph.json +1890 -0
  204. package/templates/skills/product-system/kanban.json +304 -0
  205. package/templates/skills/product-system/nodes/comp_001.md +56 -0
  206. package/templates/skills/product-system/nodes/comp_002.md +57 -0
  207. package/templates/skills/product-system/nodes/data_001.md +51 -0
  208. package/templates/skills/product-system/nodes/data_002.md +40 -0
  209. package/templates/skills/product-system/nodes/data_004.md +38 -0
  210. package/templates/skills/product-system/nodes/dec_001.md +34 -0
  211. package/templates/skills/product-system/nodes/dec_016.md +32 -0
  212. package/templates/skills/product-system/nodes/feat_008.md +30 -0
  213. package/templates/skills/video-composition-agent/SKILL.md +232 -0
  214. package/templates/skills/video-script-writer/SKILL.md +136 -0
@@ -54,12 +54,12 @@ export function LoginPage({ onLoginSuccess }: LoginPageProps) {
54
54
  }, [email, password, onLoginSuccess]);
55
55
 
56
56
  return (
57
- <div className="login-page">
58
- <div className="login-card">
59
- <div className="login-header">
60
- <span className="login-title">login</span>
57
+ <div className="flex items-center justify-center w-full h-screen bg-surface">
58
+ <div className="w-[360px] p-8 bg-surface-alt border border-edge rounded">
59
+ <div className="flex items-center justify-between mb-6">
60
+ <span className="font-mono text-base text-content tracking-wide">login</span>
61
61
  <button
62
- className="login-theme-toggle"
62
+ className="bg-transparent border border-edge rounded w-7 h-7 text-content-secondary text-sm cursor-pointer flex items-center justify-center transition-[border-color,color] duration-150 hover:border-content hover:text-content"
63
63
  onClick={toggleTheme}
64
64
  title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} theme`}
65
65
  >
@@ -67,11 +67,11 @@ export function LoginPage({ onLoginSuccess }: LoginPageProps) {
67
67
  </button>
68
68
  </div>
69
69
 
70
- <form className="login-form" onSubmit={handleSubmit}>
71
- <label className="login-label" htmlFor="login-email">email</label>
70
+ <form className="flex flex-col gap-2" onSubmit={handleSubmit}>
71
+ <label className="font-mono text-[11px] text-content-secondary mt-1" htmlFor="login-email">email</label>
72
72
  <input
73
73
  id="login-email"
74
- className="login-input"
74
+ className="bg-surface-raised border border-edge rounded text-content font-mono text-[13px] py-2 px-2.5 outline-none transition-[border-color] duration-150 focus:border-accent placeholder:text-content-muted disabled:opacity-60"
75
75
  type="email"
76
76
  value={email}
77
77
  onChange={e => setEmail(e.target.value)}
@@ -81,10 +81,10 @@ export function LoginPage({ onLoginSuccess }: LoginPageProps) {
81
81
  disabled={submitting}
82
82
  />
83
83
 
84
- <label className="login-label" htmlFor="login-password">password</label>
84
+ <label className="font-mono text-[11px] text-content-secondary mt-1" htmlFor="login-password">password</label>
85
85
  <input
86
86
  id="login-password"
87
- className="login-input"
87
+ className="bg-surface-raised border border-edge rounded text-content font-mono text-[13px] py-2 px-2.5 outline-none transition-[border-color] duration-150 focus:border-accent placeholder:text-content-muted disabled:opacity-60"
88
88
  type="password"
89
89
  value={password}
90
90
  onChange={e => setPassword(e.target.value)}
@@ -93,10 +93,10 @@ export function LoginPage({ onLoginSuccess }: LoginPageProps) {
93
93
  disabled={submitting}
94
94
  />
95
95
 
96
- {error && <div className="login-error">{error}</div>}
96
+ {error && <div className="font-mono text-[11px] text-error mt-1">{error}</div>}
97
97
 
98
98
  <button
99
- className="login-submit"
99
+ className="mt-3 bg-transparent border border-accent rounded text-accent font-mono text-[13px] py-2 px-3.5 cursor-pointer transition-[background,color] duration-150 hover:enabled:bg-accent hover:enabled:text-white disabled:opacity-50 disabled:cursor-not-allowed"
100
100
  type="submit"
101
101
  disabled={submitting}
102
102
  >
@@ -104,10 +104,10 @@ export function LoginPage({ onLoginSuccess }: LoginPageProps) {
104
104
  </button>
105
105
  </form>
106
106
 
107
- <p className="login-register-link">
107
+ <p className="font-mono text-xs text-content-secondary mt-4 text-center">
108
108
  <Link to="/forgot-password">Forgot password?</Link>
109
109
  </p>
110
- <p className="login-register-link">
110
+ <p className="font-mono text-xs text-content-secondary mt-2 text-center">
111
111
  No account yet? <Link to="/register">Register</Link>
112
112
  </p>
113
113
  </div>
@@ -5,7 +5,7 @@ interface ProjectSelectorProps {
5
5
  projects: Project[];
6
6
  selectedProjectId: string | null;
7
7
  onSelect: (id: string) => void;
8
- onCreate: (name: string) => Promise<any>;
8
+ onCreate: (name: string, type?: string) => Promise<any>;
9
9
  onRename: (id: string, name: string) => Promise<void>;
10
10
  onArchive: (id: string) => Promise<void>;
11
11
  onOpenGitModal?: (project: Project) => void;
@@ -17,6 +17,7 @@ export function ProjectSelector({
17
17
  const [open, setOpen] = useState(false);
18
18
  const [creating, setCreating] = useState(false);
19
19
  const [newName, setNewName] = useState('');
20
+ const [newType, setNewType] = useState<'software' | 'video'>('software');
20
21
  const [renamingId, setRenamingId] = useState<string | null>(null);
21
22
  const [renameValue, setRenameValue] = useState('');
22
23
  const dropdownRef = useRef<HTMLDivElement>(null);
@@ -53,8 +54,9 @@ export function ProjectSelector({
53
54
  const trimmed = newName.trim();
54
55
  if (!trimmed) return;
55
56
  try {
56
- await onCreate(trimmed);
57
+ await onCreate(trimmed, newType);
57
58
  setNewName('');
59
+ setNewType('software');
58
60
  setCreating(false);
59
61
  setOpen(false);
60
62
  } catch {
@@ -83,30 +85,30 @@ export function ProjectSelector({
83
85
  };
84
86
 
85
87
  return (
86
- <div className="project-selector" ref={dropdownRef}>
88
+ <div className="relative" ref={dropdownRef}>
87
89
  <button
88
- className="project-selector-trigger"
90
+ className="flex items-center gap-1.5 py-[5px] px-2.5 bg-transparent border border-edge rounded text-content font-mono text-xs cursor-pointer transition-[border-color,background] duration-150 max-w-[200px] hover:bg-tab-hover hover:border-accent"
89
91
  onClick={() => setOpen(v => !v)}
90
92
  title="Select project"
91
93
  >
92
- <span className="project-selector-label">
94
+ <span className="overflow-hidden text-ellipsis whitespace-nowrap">
93
95
  {selectedProject?.name || 'No project'}
94
96
  </span>
95
- <span className="project-selector-chevron">{open ? '\u25B4' : '\u25BE'}</span>
97
+ <span className="text-[10px] text-content-muted shrink-0">{open ? '\u25B4' : '\u25BE'}</span>
96
98
  </button>
97
99
 
98
100
  {open && (
99
- <div className="project-selector-dropdown">
100
- <div className="project-selector-list">
101
+ <div className="absolute top-[calc(100%+4px)] left-0 min-w-[220px] max-w-[300px] bg-panel border border-edge rounded-md shadow-[0_4px_12px_var(--panel-shadow)] z-[200] overflow-hidden">
102
+ <div className="max-h-60 overflow-y-auto py-1">
101
103
  {projects.map(project => (
102
104
  <div
103
105
  key={project.id}
104
- className={`project-selector-item${project.id === selectedProjectId ? ' active' : ''}`}
106
+ className={`group flex items-center pr-1 hover:bg-tab-hover${project.id === selectedProjectId ? ' bg-tab-active' : ''}`}
105
107
  >
106
108
  {renamingId === project.id ? (
107
109
  <input
108
110
  ref={renameInputRef}
109
- className="project-selector-input"
111
+ className="flex-1 py-[5px] px-2 bg-surface border border-accent rounded-sm text-content font-mono text-xs outline-none mx-1 my-0.5"
110
112
  value={renameValue}
111
113
  onChange={e => setRenameValue(e.target.value)}
112
114
  onKeyDown={e => {
@@ -118,15 +120,18 @@ export function ProjectSelector({
118
120
  ) : (
119
121
  <>
120
122
  <button
121
- className="project-selector-item-name"
123
+ className="flex-1 flex items-center gap-1.5 text-left py-1.5 px-2 bg-none border-none text-content font-mono text-xs cursor-pointer overflow-hidden"
122
124
  onClick={() => { onSelect(project.id); setOpen(false); }}
123
125
  >
124
- {project.name}
126
+ <span className="overflow-hidden text-ellipsis whitespace-nowrap">{project.name}</span>
127
+ {project.type === 'video' && (
128
+ <span className="shrink-0 rounded-full bg-purple-500/15 px-1.5 py-0.5 text-[9px] font-bold text-purple-400">Video</span>
129
+ )}
125
130
  </button>
126
- <div className="project-selector-item-actions">
131
+ <div className="flex gap-0.5 opacity-0 transition-opacity duration-150 group-hover:opacity-100">
127
132
  {onOpenGitModal && (
128
133
  <button
129
- className={`project-selector-action-btn project-selector-git-btn${project.repoUrl ? ' connected' : ''}`}
134
+ className={`bg-none border-none text-xs cursor-pointer px-1 py-0.5 rounded-sm transition-[color,background] duration-150 hover:text-content hover:bg-surface-raised ${project.repoUrl ? 'text-accent' : 'text-content-secondary'}`}
130
135
  title="Git Repository"
131
136
  onClick={(e) => {
132
137
  e.stopPropagation();
@@ -140,7 +145,7 @@ export function ProjectSelector({
140
145
  </button>
141
146
  )}
142
147
  <button
143
- className="project-selector-action-btn"
148
+ className="bg-none border-none text-content-muted text-xs cursor-pointer px-1 py-0.5 rounded-sm transition-[color,background] duration-150 hover:text-content hover:bg-surface-raised"
144
149
  title="Rename"
145
150
  onClick={(e) => {
146
151
  e.stopPropagation();
@@ -152,7 +157,7 @@ export function ProjectSelector({
152
157
  </button>
153
158
  {!project.isDefault && (
154
159
  <button
155
- className="project-selector-action-btn project-selector-archive-btn"
160
+ className="bg-none border-none text-content-muted text-xs cursor-pointer px-1 py-0.5 rounded-sm transition-[color,background] duration-150 hover:text-content hover:bg-surface-raised hover:!text-error"
156
161
  title="Archive"
157
162
  onClick={(e) => {
158
163
  e.stopPropagation();
@@ -169,27 +174,43 @@ export function ProjectSelector({
169
174
  ))}
170
175
  </div>
171
176
 
172
- <div className="project-selector-footer">
177
+ <div className="border-t border-edge p-1">
173
178
  {creating ? (
174
- <div className="project-selector-create-form">
175
- <input
176
- ref={createInputRef}
177
- className="project-selector-input"
178
- placeholder="Project name..."
179
- value={newName}
180
- onChange={e => setNewName(e.target.value)}
181
- onKeyDown={e => {
182
- if (e.key === 'Enter') handleCreate();
183
- if (e.key === 'Escape') { setCreating(false); setNewName(''); }
184
- }}
185
- />
186
- <button className="project-selector-create-confirm" onClick={handleCreate}>
187
- +
188
- </button>
179
+ <div className="flex flex-col gap-1">
180
+ <div className="flex gap-1 items-center">
181
+ <input
182
+ ref={createInputRef}
183
+ className="flex-1 py-[5px] px-2 bg-surface border border-accent rounded-sm text-content font-mono text-xs outline-none mx-1 my-0.5"
184
+ placeholder="Project name..."
185
+ value={newName}
186
+ onChange={e => setNewName(e.target.value)}
187
+ onKeyDown={e => {
188
+ if (e.key === 'Enter') handleCreate();
189
+ if (e.key === 'Escape') { setCreating(false); setNewName(''); setNewType('software'); }
190
+ }}
191
+ />
192
+ <button className="bg-accent border-none text-white text-sm font-bold w-7 h-7 rounded-sm cursor-pointer flex items-center justify-center shrink-0" onClick={handleCreate}>
193
+ +
194
+ </button>
195
+ </div>
196
+ <div className="flex gap-1 px-1.5">
197
+ <button
198
+ className={`px-2 py-0.5 rounded-sm text-[10px] font-mono border cursor-pointer transition-colors ${newType === 'software' ? 'bg-accent/15 border-accent text-accent' : 'bg-transparent border-edge text-content-muted hover:border-content/30'}`}
199
+ onClick={() => setNewType('software')}
200
+ >
201
+ Software
202
+ </button>
203
+ <button
204
+ className={`px-2 py-0.5 rounded-sm text-[10px] font-mono border cursor-pointer transition-colors ${newType === 'video' ? 'bg-accent/15 border-accent text-accent' : 'bg-transparent border-edge text-content-muted hover:border-content/30'}`}
205
+ onClick={() => setNewType('video')}
206
+ >
207
+ Video
208
+ </button>
209
+ </div>
189
210
  </div>
190
211
  ) : (
191
212
  <button
192
- className="project-selector-new-btn"
213
+ className="w-full py-1.5 px-2 bg-none border-none text-accent font-mono text-xs cursor-pointer text-left rounded-sm transition-background duration-150 hover:bg-tab-hover"
193
214
  onClick={() => setCreating(true)}
194
215
  >
195
216
  + New Project
@@ -1,22 +1,14 @@
1
- import React, { useState, useEffect, useRef, useCallback } from 'react';
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
2
  import { apiClient } from '../api/client';
3
+ import { useQaSheetStore } from '../stores/useQaSheetStore';
4
+ import { SideSheet } from './ds/SideSheet';
3
5
 
4
- interface QaIssueSheetProps {
5
- isOpen: boolean;
6
- featureId: string | null;
7
- featureName: string;
8
- column: string;
9
- notes: any[];
10
- reviews: any[];
11
- onClose: () => void;
12
- onNotesChanged: () => void;
13
- }
6
+ export function QaIssueSheet() {
7
+ const {
8
+ isOpen, featureId, featureName, column, notes: initialNotes,
9
+ reviews: initialReviews, close,
10
+ } = useQaSheetStore();
14
11
 
15
- export function QaIssueSheet({
16
- isOpen, featureId, featureName, column, notes: initialNotes, reviews: initialReviews,
17
- onClose, onNotesChanged,
18
- }: QaIssueSheetProps) {
19
- const sheetRef = useRef<HTMLDivElement>(null);
20
12
  const [notes, setNotes] = useState<any[]>(initialNotes);
21
13
  const [reviews, setReviews] = useState<any[]>(initialReviews);
22
14
  const [newNoteText, setNewNoteText] = useState('');
@@ -25,7 +17,7 @@ export function QaIssueSheet({
25
17
 
26
18
  const isEditable = column === 'qa';
27
19
 
28
- // Sync when props change
20
+ // Sync when store data changes
29
21
  useEffect(() => {
30
22
  setNotes(initialNotes);
31
23
  setReviews(initialReviews);
@@ -33,19 +25,6 @@ export function QaIssueSheet({
33
25
  setNewNoteText('');
34
26
  }, [featureId, initialNotes, initialReviews]);
35
27
 
36
- // Close on outside click
37
- useEffect(() => {
38
- if (!isOpen) return;
39
- const handleClick = (e: MouseEvent) => {
40
- if (sheetRef.current && !sheetRef.current.contains(e.target as Node)) {
41
- if ((e.target as HTMLElement).closest('.kanban-issues-btn')) return;
42
- onClose();
43
- }
44
- };
45
- document.addEventListener('click', handleClick);
46
- return () => document.removeEventListener('click', handleClick);
47
- }, [isOpen, onClose]);
48
-
49
28
  const refreshNotes = useCallback(async () => {
50
29
  if (!featureId) return;
51
30
  try {
@@ -55,11 +34,10 @@ export function QaIssueSheet({
55
34
  setNotes(entry.notes || []);
56
35
  setReviews(entry.reviews || []);
57
36
  }
58
- onNotesChanged();
59
37
  } catch (err) {
60
38
  console.error('Failed to refresh notes:', err);
61
39
  }
62
- }, [featureId, onNotesChanged]);
40
+ }, [featureId]);
63
41
 
64
42
  const handleAddNote = async () => {
65
43
  const text = newNoteText.trim();
@@ -105,63 +83,59 @@ export function QaIssueSheet({
105
83
  };
106
84
 
107
85
  return (
108
- <div className={`qa-issue-sheet${isOpen ? ' open' : ''}`} ref={sheetRef}>
109
- <div className="qa-sheet-header">
110
- <span className="qa-sheet-title">{featureId} {featureName}</span>
111
- <button className="qa-sheet-close" onClick={onClose}>&times;</button>
112
- </div>
113
- <div className="qa-sheet-body">
114
- <div className="qa-sheet-subtitle">
86
+ <SideSheet isOpen={isOpen} onClose={close} title={`${featureId} ${featureName}`} zIndex={260}>
87
+ <div className="flex flex-col gap-3">
88
+ <div className="text-[11px] text-content-muted uppercase tracking-wider">
115
89
  {isEditable ? 'Report and manage QA issues' : 'Issues (read-only)'}
116
90
  </div>
117
91
 
118
92
  {reviews.length > 0 && (
119
- <div className="qa-sheet-review-history">
120
- <div className="qa-sheet-section-title">Review History</div>
93
+ <div className="mb-4 border-b border-edge pb-3">
94
+ <div className="text-[11px] font-semibold uppercase tracking-wide text-content-secondary mb-2">Review History</div>
121
95
  {reviews.map((review: any, i: number) => (
122
- <div key={i} className="qa-sheet-review-item">
123
- <div className="qa-sheet-review-header">
124
- <span className="qa-sheet-review-cycle">Cycle {review.cycle}</span>
125
- <span className={`qa-sheet-review-badge ${review.status}`}>
96
+ <div key={i} className="bg-surface-alt rounded-md px-2.5 py-2 mb-1.5">
97
+ <div className="flex items-center gap-2 text-xs">
98
+ <span className="font-semibold text-content">Cycle {review.cycle}</span>
99
+ <span className={`text-[10px] font-semibold px-1.5 py-px rounded-sm uppercase ${review.status === 'approved' ? 'bg-[#16a34a22] text-[#16a34a]' : 'bg-[#dc262622] text-[#dc2626]'}`}>
126
100
  {review.status === 'approved' ? 'Approved' : 'Rejected'}
127
101
  </span>
128
- <span className="qa-sheet-review-time">{formatDate(review.timestamp)}</span>
102
+ <span className="text-content-secondary text-[11px] ml-auto">{formatDate(review.timestamp)}</span>
129
103
  </div>
130
- {review.reason && <div className="qa-sheet-review-reason">{review.reason}</div>}
104
+ {review.reason && <div className="mt-1.5 text-xs text-content-secondary whitespace-pre-wrap leading-snug max-h-[120px] overflow-y-auto">{review.reason}</div>}
131
105
  </div>
132
106
  ))}
133
107
  </div>
134
108
  )}
135
109
 
136
110
  {notes.length === 0 ? (
137
- <div className="qa-sheet-empty">No issues reported yet.</div>
111
+ <div className="text-xs text-content-muted p-4 text-center">No issues reported yet.</div>
138
112
  ) : (
139
- <div className="qa-sheet-notes-list">
113
+ <div className="flex flex-col gap-2">
140
114
  {notes.map((note: any) => (
141
- <div key={note.id} className="qa-sheet-note">
115
+ <div key={note.id} className="bg-surface-raised border border-edge rounded px-3 py-2.5">
142
116
  {editingNoteId === note.id ? (
143
- <div className="qa-sheet-edit-form">
117
+ <div className="flex flex-col gap-2">
144
118
  <textarea
145
- className="qa-sheet-textarea"
119
+ className="w-full bg-surface-raised border border-edge rounded text-content font-mono text-xs px-3 py-2.5 resize-y leading-relaxed focus:outline-none focus:border-accent placeholder:text-content-muted"
146
120
  value={editText}
147
121
  onChange={e => setEditText(e.target.value)}
148
122
  rows={4}
149
123
  autoFocus
150
124
  />
151
- <div className="qa-sheet-form-btns">
152
- <button className="qa-sheet-add-btn" onClick={() => handleUpdateNote(note.id)}>Save</button>
153
- <button className="qa-sheet-cancel-btn" onClick={() => setEditingNoteId(null)}>Cancel</button>
125
+ <div className="flex gap-2">
126
+ <button className="bg-transparent border border-accent rounded text-accent font-mono text-[11px] px-3.5 py-1.5 cursor-pointer transition-[background,color] duration-150 hover:bg-accent hover:text-white" onClick={() => handleUpdateNote(note.id)}>Save</button>
127
+ <button className="bg-transparent border border-edge rounded text-content-secondary font-mono text-[11px] px-3.5 py-1.5 cursor-pointer hover:border-content hover:text-content" onClick={() => setEditingNoteId(null)}>Cancel</button>
154
128
  </div>
155
129
  </div>
156
130
  ) : (
157
131
  <>
158
- <div className="qa-sheet-note-text">{note.text}</div>
159
- <div className="qa-sheet-note-meta">
160
- <span className="qa-sheet-note-date">{formatDate(note.created_at || note.timestamp)}</span>
132
+ <div className="text-xs text-content leading-relaxed whitespace-pre-wrap break-words">{note.text}</div>
133
+ <div className="flex items-center justify-between mt-2">
134
+ <span className="text-[10px] text-content-muted">{formatDate(note.created_at || note.timestamp)}</span>
161
135
  {isEditable && (
162
- <span className="qa-sheet-note-actions">
163
- <button className="qa-sheet-action-btn" onClick={() => { setEditingNoteId(note.id); setEditText(note.text); }}>Edit</button>
164
- <button className="qa-sheet-action-btn delete" onClick={() => handleDeleteNote(note.id)}>Delete</button>
136
+ <span className="flex gap-1.5">
137
+ <button className="bg-transparent border-none text-content-muted text-[11px] font-mono cursor-pointer px-1.5 py-0.5 rounded-sm hover:bg-surface hover:text-content" onClick={() => { setEditingNoteId(note.id); setEditText(note.text); }}>Edit</button>
138
+ <button className="bg-transparent border-none text-content-muted text-[11px] font-mono cursor-pointer px-1.5 py-0.5 rounded-sm hover:bg-surface hover:text-error" onClick={() => handleDeleteNote(note.id)}>Delete</button>
165
139
  </span>
166
140
  )}
167
141
  </div>
@@ -173,20 +147,20 @@ export function QaIssueSheet({
173
147
  )}
174
148
 
175
149
  {isEditable && (
176
- <div className="qa-sheet-add-form">
150
+ <div className="flex flex-col gap-2 mt-auto pt-3 border-t border-edge">
177
151
  <textarea
178
- className="qa-sheet-textarea"
152
+ className="w-full bg-surface-raised border border-edge rounded text-content font-mono text-xs px-3 py-2.5 resize-y leading-relaxed focus:outline-none focus:border-accent placeholder:text-content-muted"
179
153
  placeholder="Describe the issue..."
180
154
  rows={4}
181
155
  value={newNoteText}
182
156
  onChange={e => setNewNoteText(e.target.value)}
183
157
  />
184
- <div className="qa-sheet-form-btns">
185
- <button className="qa-sheet-add-btn" onClick={handleAddNote}>Add Issue</button>
158
+ <div className="flex gap-2">
159
+ <button className="bg-transparent border border-accent rounded text-accent font-mono text-[11px] px-3.5 py-1.5 cursor-pointer transition-[background,color] duration-150 hover:bg-accent hover:text-white" onClick={handleAddNote}>Add Issue</button>
186
160
  </div>
187
161
  </div>
188
162
  )}
189
163
  </div>
190
- </div>
164
+ </SideSheet>
191
165
  );
192
166
  }