@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
@@ -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,6 +1,7 @@
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
3
  import { useQaSheetStore } from '../stores/useQaSheetStore';
4
+ import { SideSheet } from './ds/SideSheet';
4
5
 
5
6
  export function QaIssueSheet() {
6
7
  const {
@@ -8,7 +9,6 @@ export function QaIssueSheet() {
8
9
  reviews: initialReviews, close,
9
10
  } = useQaSheetStore();
10
11
 
11
- const sheetRef = useRef<HTMLDivElement>(null);
12
12
  const [notes, setNotes] = useState<any[]>(initialNotes);
13
13
  const [reviews, setReviews] = useState<any[]>(initialReviews);
14
14
  const [newNoteText, setNewNoteText] = useState('');
@@ -25,19 +25,6 @@ export function QaIssueSheet() {
25
25
  setNewNoteText('');
26
26
  }, [featureId, initialNotes, initialReviews]);
27
27
 
28
- // Close on outside click
29
- useEffect(() => {
30
- if (!isOpen) return;
31
- const handleClick = (e: MouseEvent) => {
32
- if (sheetRef.current && !sheetRef.current.contains(e.target as Node)) {
33
- if ((e.target as HTMLElement).closest('.kanban-issues-btn')) return;
34
- close();
35
- }
36
- };
37
- document.addEventListener('click', handleClick);
38
- return () => document.removeEventListener('click', handleClick);
39
- }, [isOpen, close]);
40
-
41
28
  const refreshNotes = useCallback(async () => {
42
29
  if (!featureId) return;
43
30
  try {
@@ -96,63 +83,59 @@ export function QaIssueSheet() {
96
83
  };
97
84
 
98
85
  return (
99
- <div className={`qa-issue-sheet${isOpen ? ' open' : ''}`} ref={sheetRef}>
100
- <div className="qa-sheet-header">
101
- <span className="qa-sheet-title">{featureId} {featureName}</span>
102
- <button className="qa-sheet-close" onClick={close}>&times;</button>
103
- </div>
104
- <div className="qa-sheet-body">
105
- <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">
106
89
  {isEditable ? 'Report and manage QA issues' : 'Issues (read-only)'}
107
90
  </div>
108
91
 
109
92
  {reviews.length > 0 && (
110
- <div className="qa-sheet-review-history">
111
- <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>
112
95
  {reviews.map((review: any, i: number) => (
113
- <div key={i} className="qa-sheet-review-item">
114
- <div className="qa-sheet-review-header">
115
- <span className="qa-sheet-review-cycle">Cycle {review.cycle}</span>
116
- <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]'}`}>
117
100
  {review.status === 'approved' ? 'Approved' : 'Rejected'}
118
101
  </span>
119
- <span className="qa-sheet-review-time">{formatDate(review.timestamp)}</span>
102
+ <span className="text-content-secondary text-[11px] ml-auto">{formatDate(review.timestamp)}</span>
120
103
  </div>
121
- {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>}
122
105
  </div>
123
106
  ))}
124
107
  </div>
125
108
  )}
126
109
 
127
110
  {notes.length === 0 ? (
128
- <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>
129
112
  ) : (
130
- <div className="qa-sheet-notes-list">
113
+ <div className="flex flex-col gap-2">
131
114
  {notes.map((note: any) => (
132
- <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">
133
116
  {editingNoteId === note.id ? (
134
- <div className="qa-sheet-edit-form">
117
+ <div className="flex flex-col gap-2">
135
118
  <textarea
136
- 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"
137
120
  value={editText}
138
121
  onChange={e => setEditText(e.target.value)}
139
122
  rows={4}
140
123
  autoFocus
141
124
  />
142
- <div className="qa-sheet-form-btns">
143
- <button className="qa-sheet-add-btn" onClick={() => handleUpdateNote(note.id)}>Save</button>
144
- <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>
145
128
  </div>
146
129
  </div>
147
130
  ) : (
148
131
  <>
149
- <div className="qa-sheet-note-text">{note.text}</div>
150
- <div className="qa-sheet-note-meta">
151
- <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>
152
135
  {isEditable && (
153
- <span className="qa-sheet-note-actions">
154
- <button className="qa-sheet-action-btn" onClick={() => { setEditingNoteId(note.id); setEditText(note.text); }}>Edit</button>
155
- <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>
156
139
  </span>
157
140
  )}
158
141
  </div>
@@ -164,20 +147,20 @@ export function QaIssueSheet() {
164
147
  )}
165
148
 
166
149
  {isEditable && (
167
- <div className="qa-sheet-add-form">
150
+ <div className="flex flex-col gap-2 mt-auto pt-3 border-t border-edge">
168
151
  <textarea
169
- 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"
170
153
  placeholder="Describe the issue..."
171
154
  rows={4}
172
155
  value={newNoteText}
173
156
  onChange={e => setNewNoteText(e.target.value)}
174
157
  />
175
- <div className="qa-sheet-form-btns">
176
- <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>
177
160
  </div>
178
161
  </div>
179
162
  )}
180
163
  </div>
181
- </div>
164
+ </SideSheet>
182
165
  );
183
166
  }
@@ -3,7 +3,8 @@ import { marked } from 'marked';
3
3
  import { useSidePanelStore } from '../stores/useSidePanelStore';
4
4
  import { useGraphStore } from '../stores/useGraphStore';
5
5
  import { useGraphUIStore } from '../stores/useGraphUIStore';
6
- import { getTaskCssClass, getTaskIcon, shouldShowTaskList } from '../utils/task_status';
6
+ import { getTaskCssClass, getTaskIcon, getTaskIconClass, getTaskNameClass, shouldShowTaskList } from '../utils/task_status';
7
+ import { SideSheet } from './ds/SideSheet';
7
8
 
8
9
  export function SidePanel() {
9
10
  const {
@@ -32,14 +33,12 @@ export function SidePanel() {
32
33
  return edges;
33
34
  };
34
35
 
36
+ const title = node ? `${node.name} (${node.id})` : '';
37
+
35
38
  if (!node) return (
36
- <div className={`side-panel${isOpen ? ' open' : ''}`} id="side-panel">
37
- <div className="panel-header">
38
- <span className="panel-title" />
39
- <button className="panel-close" onClick={close}>&times;</button>
40
- </div>
41
- <div className="panel-body" />
42
- </div>
39
+ <SideSheet isOpen={isOpen} onClose={close} title="" zIndex={200}>
40
+ <div className="panel-body text-sm leading-relaxed" />
41
+ </SideSheet>
43
42
  );
44
43
 
45
44
  const statusLabel = (node.status || 'draft').replace(/_/g, ' ');
@@ -48,34 +47,30 @@ export function SidePanel() {
48
47
  const edges = findEdges(node.id);
49
48
 
50
49
  return (
51
- <div className={`side-panel${isOpen ? ' open' : ''}`} id="side-panel">
52
- <div className="panel-header">
53
- <span className="panel-title">{node.name} ({node.id})</span>
54
- <button className="panel-close" onClick={close}>&times;</button>
55
- </div>
56
- <div className="panel-body">
57
- <div className="panel-status-section">
58
- <span className="panel-type-badge">{typeLabel}</span>
59
- <span className={`panel-status-badge panel-status-${node.status || 'draft'}`}>{statusLabel}</span>
60
- <span className="panel-completeness">{completeness}% complete</span>
50
+ <SideSheet isOpen={isOpen} onClose={close} title={title} zIndex={200}>
51
+ <div className="panel-body text-sm leading-relaxed">
52
+ <div className="flex items-center gap-2 mb-3 pb-2.5 border-b border-edge flex-wrap">
53
+ <span className="text-[10px] text-content-muted uppercase tracking-wide bg-surface-raised px-2 py-0.5 rounded-sm">{typeLabel}</span>
54
+ <span className={`text-[10px] px-2 py-0.5 rounded-sm font-semibold capitalize panel-status-${node.status || 'draft'}`}>{statusLabel}</span>
55
+ <span className="text-[11px] text-content-secondary ml-auto">{completeness}% complete</span>
61
56
  </div>
62
57
  {shouldShowTaskList(tasks) && (
63
- <div className="panel-task-list">
58
+ <div className="my-2 border border-edge rounded">
64
59
  <button
65
- className="panel-task-list-toggle"
60
+ className="bg-none border-none text-content font-mono text-xs cursor-pointer py-1.5 px-2 w-full text-left flex items-center gap-1.5 hover:text-content hover:bg-surface-raised"
66
61
  onClick={toggleTasks}
67
62
  >
68
- <span className={`panel-task-list-chevron${expandedTasks ? ' expanded' : ''}`}>{'\u25B6'}</span>
63
+ <span className={`inline-block text-[9px] transition-transform duration-150${expandedTasks ? ' rotate-90' : ''}`}>{'\u25B6'}</span>
69
64
  Tasks ({tasks!.completed}/{tasks!.total})
70
65
  </button>
71
66
  {expandedTasks && tasks!.items?.length > 0 && (
72
- <div className="panel-task-list-items">
67
+ <div className="px-2 pt-1 pb-1.5 flex flex-col gap-[3px] border-t border-edge">
73
68
  {tasks!.items.map((task: any, idx: number) => (
74
69
  <div key={idx} className={getTaskCssClass(task.status)}>
75
- <span className="kanban-task-icon">
70
+ <span className={`shrink-0 w-3 text-center text-[10px] ${getTaskIconClass(task.status)}`}>
76
71
  {getTaskIcon(task.status)}
77
72
  </span>
78
- <span className="kanban-task-name">{task.name}</span>
73
+ <span className={getTaskNameClass(task.status)}>{task.name}</span>
79
74
  </div>
80
75
  ))}
81
76
  </div>
@@ -84,21 +79,21 @@ export function SidePanel() {
84
79
  )}
85
80
  <div dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }} />
86
81
  {workSummaries.length > 0 && (
87
- <div className="panel-work-summary">
82
+ <div className="mt-3 border-t border-edge pt-2">
88
83
  <button
89
- className="panel-work-summary-toggle"
84
+ className="bg-none border-none text-content-secondary text-[13px] font-mono cursor-pointer py-1 w-full text-left hover:text-content"
90
85
  onClick={toggleSummaries}
91
86
  >
92
87
  {expandedSummaries ? '\u25BC' : '\u25B6'} Work Summary ({workSummaries.length} cycle{workSummaries.length !== 1 ? 's' : ''})
93
88
  </button>
94
89
  {expandedSummaries && (
95
- <div className="panel-work-summary-details">
90
+ <div className="mt-2 text-xs text-content-secondary">
96
91
  {workSummaries.map((ws, idx) => (
97
- <div key={idx} className="panel-work-summary-cycle">
98
- <div className="panel-work-summary-cycle-header">Cycle {ws.cycle}</div>
92
+ <div key={idx} className="mb-3 pb-2 border-b border-edge last:border-b-0 last:mb-0">
93
+ <div className="font-semibold font-mono text-content mb-1">Cycle {ws.cycle}</div>
99
94
  {ws.filesCreated?.length > 0 && (
100
- <div className="panel-work-summary-files panel-work-summary-files-created">
101
- <span className="panel-work-summary-label">Created:</span>
95
+ <div>
96
+ <span className="font-semibold text-content-secondary font-mono text-[11px] uppercase tracking-wider" style={{ color: '#4caf50' }}>Created:</span>
102
97
  <ul>
103
98
  {ws.filesCreated.map((f, fi) => (
104
99
  <li key={fi}>{f}</li>
@@ -107,8 +102,8 @@ export function SidePanel() {
107
102
  </div>
108
103
  )}
109
104
  {ws.filesUpdated?.length > 0 && (
110
- <div className="panel-work-summary-files panel-work-summary-files-updated">
111
- <span className="panel-work-summary-label">Updated:</span>
105
+ <div>
106
+ <span className="font-semibold text-content-secondary font-mono text-[11px] uppercase tracking-wider" style={{ color: '#ff9800' }}>Updated:</span>
112
107
  <ul>
113
108
  {ws.filesUpdated.map((f, fi) => (
114
109
  <li key={fi}>{f}</li>
@@ -117,8 +112,8 @@ export function SidePanel() {
117
112
  </div>
118
113
  )}
119
114
  {ws.filesDeleted?.length > 0 && (
120
- <div className="panel-work-summary-files panel-work-summary-files-deleted">
121
- <span className="panel-work-summary-label">Deleted:</span>
115
+ <div>
116
+ <span className="font-semibold text-content-secondary font-mono text-[11px] uppercase tracking-wider" style={{ color: '#f44336' }}>Deleted:</span>
122
117
  <ul>
123
118
  {ws.filesDeleted.map((f, fi) => (
124
119
  <li key={fi}>{f}</li>
@@ -127,14 +122,14 @@ export function SidePanel() {
127
122
  </div>
128
123
  )}
129
124
  {ws.approach && (
130
- <div className="panel-work-summary-approach">
131
- <span className="panel-work-summary-label">Approach:</span>
125
+ <div>
126
+ <span className="font-semibold text-content-secondary font-mono text-[11px] uppercase tracking-wider">Approach:</span>
132
127
  <p>{ws.approach}</p>
133
128
  </div>
134
129
  )}
135
130
  {ws.decisions?.length > 0 && (
136
- <div className="panel-work-summary-decisions">
137
- <span className="panel-work-summary-label">Decisions:</span>
131
+ <div>
132
+ <span className="font-semibold text-content-secondary font-mono text-[11px] uppercase tracking-wider">Decisions:</span>
138
133
  <ul>
139
134
  {ws.decisions.map((d, di) => (
140
135
  <li key={di}>{d}</li>
@@ -149,21 +144,21 @@ export function SidePanel() {
149
144
  </div>
150
145
  )}
151
146
  {edges.length > 0 && (
152
- <div className="panel-edges-section">
153
- <h3>Relationships</h3>
154
- <ul className="panel-edge-list">
147
+ <div className="mt-4 pt-3 border-t border-edge">
148
+ <h3 className="text-xs text-content mb-2 uppercase tracking-wide">Relationships</h3>
149
+ <ul className="list-none p-0 m-0 flex flex-col gap-1">
155
150
  {edges.map((edge, i) => {
156
151
  const neighborNode = graphData?.nodes.find((n: any) => n.id === edge.neighborId);
157
152
  const name = neighborNode ? neighborNode.name : edge.neighborId;
158
153
  const direction = edge.direction === 'outgoing' ? '\u2192' : '\u2190';
159
154
  return (
160
- <li key={i} className="panel-edge-item">
161
- <span className="panel-edge-direction">{direction}</span>
162
- <span className="panel-edge-relation">{edge.relation.replace(/_/g, ' ')}</span>
163
- <a className="panel-edge-link" href="#" onClick={(e) => { e.preventDefault(); onEdgeClick?.(edge.neighborId); }}>
155
+ <li key={i} className="flex items-center gap-1.5 px-1.5 py-1 rounded-sm transition-background duration-150 hover:bg-surface-raised">
156
+ <span className="text-[11px] text-content-muted w-3.5 text-center shrink-0">{direction}</span>
157
+ <span className="text-[10px] text-content-muted min-w-20 shrink-0">{edge.relation.replace(/_/g, ' ')}</span>
158
+ <a className="text-xs text-accent no-underline cursor-pointer flex-1 overflow-hidden text-ellipsis whitespace-nowrap hover:underline" href="#" onClick={(e) => { e.preventDefault(); onEdgeClick?.(edge.neighborId); }}>
164
159
  {name}
165
160
  </a>
166
- <span className="panel-edge-id">{edge.neighborId}</span>
161
+ <span className="text-[10px] text-content-muted shrink-0">{edge.neighborId}</span>
167
162
  </li>
168
163
  );
169
164
  })}
@@ -171,6 +166,6 @@ export function SidePanel() {
171
166
  </div>
172
167
  )}
173
168
  </div>
174
- </div>
169
+ </SideSheet>
175
170
  );
176
171
  }