@assistkick/create 1.9.0 → 1.11.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/src/scaffolder.d.ts +12 -1
  2. package/dist/src/scaffolder.js +40 -3
  3. package/dist/src/scaffolder.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/assistkick-product-system/package.json +1 -1
  6. package/templates/assistkick-product-system/packages/backend/package.json +1 -0
  7. package/templates/assistkick-product-system/packages/backend/src/mcp/permission_mcp_server.ts +196 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +31 -7
  9. package/templates/assistkick-product-system/packages/backend/src/routes/auth.ts +15 -12
  10. package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.test.ts +95 -0
  11. package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.ts +97 -0
  12. package/templates/assistkick-product-system/packages/backend/src/routes/chat_permission.ts +94 -0
  13. package/templates/assistkick-product-system/packages/backend/src/routes/chat_sessions.ts +189 -0
  14. package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.test.ts +131 -0
  15. package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.ts +94 -0
  16. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +12 -3
  17. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +2 -2
  18. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +391 -23
  19. package/templates/assistkick-product-system/packages/backend/src/routes/git_branches.test.ts +306 -0
  20. package/templates/assistkick-product-system/packages/backend/src/routes/git_connect.test.ts +133 -0
  21. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +66 -9
  22. package/templates/assistkick-product-system/packages/backend/src/routes/preview.ts +204 -0
  23. package/templates/assistkick-product-system/packages/backend/src/routes/projects.test.ts +205 -0
  24. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +37 -9
  25. package/templates/assistkick-product-system/packages/backend/src/routes/skills.test.ts +139 -0
  26. package/templates/assistkick-product-system/packages/backend/src/routes/skills.ts +95 -0
  27. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +5 -4
  28. package/templates/assistkick-product-system/packages/backend/src/routes/users.ts +4 -4
  29. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +8 -8
  30. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +5 -5
  31. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +6 -6
  32. package/templates/assistkick-product-system/packages/backend/src/server.ts +107 -27
  33. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +105 -203
  34. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +76 -266
  35. package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.test.ts +427 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.ts +345 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.test.ts +170 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.ts +106 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.test.ts +217 -0
  40. package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.ts +188 -0
  41. package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.test.ts +1243 -0
  42. package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.ts +894 -0
  43. package/templates/assistkick-product-system/packages/backend/src/services/coherence-review.ts +3 -3
  44. package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.test.ts +85 -0
  45. package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.ts +54 -0
  46. package/templates/assistkick-product-system/packages/backend/src/services/email_service.ts +13 -10
  47. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +11 -3
  48. package/templates/assistkick-product-system/packages/backend/src/services/invitation_service.ts +1 -1
  49. package/templates/assistkick-product-system/packages/backend/src/services/password_reset_service.ts +1 -1
  50. package/templates/assistkick-product-system/packages/backend/src/services/permission_service.test.ts +243 -0
  51. package/templates/assistkick-product-system/packages/backend/src/services/permission_service.ts +259 -0
  52. package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.test.ts +172 -0
  53. package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.ts +225 -0
  54. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +29 -0
  55. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +17 -0
  56. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +255 -0
  57. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +300 -25
  58. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +44 -0
  59. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +62 -7
  60. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +77 -6
  61. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +149 -14
  62. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +2 -1
  63. package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.test.ts +45 -0
  64. package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.ts +157 -0
  65. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +4 -3
  66. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +3 -3
  67. package/templates/assistkick-product-system/packages/frontend/package.json +5 -0
  68. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +2 -0
  69. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +336 -5
  70. package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +192 -12
  71. package/templates/assistkick-product-system/packages/frontend/src/components/AttachmentPreviewList.tsx +98 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/AutocompleteDropdown.tsx +65 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ChatAttachButton.tsx +56 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ChatDropZone.tsx +80 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageBubble.tsx +155 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageContent.tsx +182 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageInput.tsx +233 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ChatSessionSidebar.tsx +218 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ChatStopButton.tsx +32 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ChatTodoSidebar.tsx +113 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ChatView.tsx +842 -0
  82. package/templates/assistkick-product-system/packages/frontend/src/components/CommitMessageModal.tsx +82 -0
  83. package/templates/assistkick-product-system/packages/frontend/src/components/DiagramOverlay.tsx +160 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +5 -5
  85. package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +9 -10
  86. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +5 -5
  87. package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +112 -41
  88. package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +2 -2
  89. package/templates/assistkick-product-system/packages/frontend/src/components/HighlightedText.tsx +87 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/ImageLightbox.tsx +192 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +2 -2
  92. package/templates/assistkick-product-system/packages/frontend/src/components/MentionPill.tsx +33 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/MermaidBlock.tsx +148 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/PermissionDialog.tsx +91 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/PermissionModeSelector.tsx +229 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +249 -83
  97. package/templates/assistkick-product-system/packages/frontend/src/components/QueuedMessageBubble.tsx +38 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +212 -117
  99. package/templates/assistkick-product-system/packages/frontend/src/components/SystemPromptAccordion.tsx +48 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/TaskIcon.tsx +11 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +25 -9
  102. package/templates/assistkick-product-system/packages/frontend/src/components/ToolDiffView.tsx +114 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/ToolResultCard.tsx +87 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/components/ToolUseCard.tsx +149 -0
  105. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +25 -8
  106. package/templates/assistkick-product-system/packages/frontend/src/components/UnifiedGitWidget.tsx +722 -0
  107. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +2 -0
  108. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +2 -1
  109. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/ProgrammableNode.tsx +178 -0
  110. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +3 -0
  111. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +103 -9
  112. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +26 -2
  113. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +42 -1
  114. package/templates/assistkick-product-system/packages/frontend/src/hooks/useDocumentTitle.ts +11 -0
  115. package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +1 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_chat_stream.ts +826 -0
  117. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_file_tree_cache.ts +69 -0
  118. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_mention_autocomplete.ts +284 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.test.ts +183 -0
  120. package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.ts +150 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.test.ts +305 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.ts +113 -0
  123. package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.test.ts +157 -0
  124. package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.ts +95 -0
  125. package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.test.ts +65 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.ts +110 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/lib/message_queue.ts +66 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.test.ts +124 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.ts +112 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +2 -0
  131. package/templates/assistkick-product-system/packages/frontend/src/routes/ChatRoute.tsx +8 -0
  132. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +2 -0
  133. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +0 -4
  134. package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +2 -0
  135. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +2 -0
  136. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -0
  137. package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +2 -0
  138. package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +2 -0
  139. package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +2 -0
  140. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +2 -0
  141. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +2 -0
  142. package/templates/assistkick-product-system/packages/frontend/src/routes/accept_invitation.tsx +2 -0
  143. package/templates/assistkick-product-system/packages/frontend/src/routes/forgot_password.tsx +2 -0
  144. package/templates/assistkick-product-system/packages/frontend/src/routes/login.tsx +2 -0
  145. package/templates/assistkick-product-system/packages/frontend/src/routes/register.tsx +2 -0
  146. package/templates/assistkick-product-system/packages/frontend/src/routes/reset_password.tsx +2 -0
  147. package/templates/assistkick-product-system/packages/frontend/src/stores/useAttachmentStore.ts +66 -0
  148. package/templates/assistkick-product-system/packages/frontend/src/stores/useChatSessionStore.ts +107 -0
  149. package/templates/assistkick-product-system/packages/frontend/src/stores/useMessageQueueStore.ts +110 -0
  150. package/templates/assistkick-product-system/packages/frontend/src/stores/usePreviewStore.ts +78 -0
  151. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +7 -0
  152. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +6 -1
  153. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +30 -357
  154. package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.test.ts +115 -0
  155. package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.ts +91 -0
  156. package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.test.ts +30 -0
  157. package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.ts +3 -0
  158. package/templates/assistkick-product-system/packages/shared/db/migrations/0015_magenta_jazinda.sql +1 -0
  159. package/templates/assistkick-product-system/packages/shared/db/migrations/0016_giant_xorn.sql +1 -0
  160. package/templates/assistkick-product-system/packages/shared/db/migrations/0017_sloppy_mentor.sql +6 -0
  161. package/templates/assistkick-product-system/packages/shared/db/migrations/0018_vengeful_kabuki.sql +9 -0
  162. package/templates/assistkick-product-system/packages/shared/db/migrations/0019_careful_sentinels.sql +8 -0
  163. package/templates/assistkick-product-system/packages/shared/db/migrations/0020_clever_spot.sql +27 -0
  164. package/templates/assistkick-product-system/packages/shared/db/migrations/0021_graceful_hex.sql +1 -0
  165. package/templates/assistkick-product-system/packages/shared/db/migrations/0022_short_kingpin.sql +1 -0
  166. package/templates/assistkick-product-system/packages/shared/db/migrations/0023_ambiguous_sharon_carter.sql +1 -0
  167. package/templates/assistkick-product-system/packages/shared/db/migrations/0024_fat_unus.sql +1 -0
  168. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0015_snapshot.json +1552 -0
  169. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0016_snapshot.json +1560 -0
  170. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0017_snapshot.json +1598 -0
  171. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0018_snapshot.json +1657 -0
  172. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0019_snapshot.json +1709 -0
  173. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0020_snapshot.json +1733 -0
  174. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0021_snapshot.json +1740 -0
  175. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0022_snapshot.json +1755 -0
  176. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0023_snapshot.json +1762 -0
  177. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0024_snapshot.json +1769 -0
  178. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
  179. package/templates/assistkick-product-system/packages/shared/db/schema.ts +40 -1
  180. package/templates/assistkick-product-system/packages/shared/lib/claude-service.test.ts +236 -0
  181. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +46 -5
  182. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +65 -39
  183. package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.test.ts +173 -0
  184. package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.ts +213 -0
  185. package/templates/assistkick-product-system/packages/shared/lib/validator.test.ts +70 -0
  186. package/templates/assistkick-product-system/packages/shared/lib/validator.ts +17 -1
  187. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +803 -27
  188. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +502 -68
  189. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +4 -4
  190. package/templates/assistkick-product-system/packages/shared/package.json +2 -1
  191. package/templates/assistkick-product-system/packages/shared/test_fixtures/hanging_stream.mjs +46 -0
  192. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +44 -0
  193. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +7 -0
  194. package/templates/assistkick-product-system/packages/shared/tools/remove_node.ts +2 -1
  195. package/templates/assistkick-product-system/packages/shared/tools/resolve_question.ts +2 -1
  196. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -1
  197. package/templates/assistkick-product-system/tests/message_queue.test.ts +178 -0
  198. package/templates/assistkick-product-system/tests/message_queue_per_session.test.ts +143 -0
  199. package/templates/skills/assistkick-bootstrap/SKILL.md +26 -26
  200. package/templates/skills/assistkick-code-reviewer/SKILL.md +45 -46
  201. package/templates/skills/assistkick-db-explorer/SKILL.md +13 -13
  202. package/templates/skills/assistkick-debugger/SKILL.md +23 -23
  203. package/templates/skills/assistkick-developer/SKILL.md +59 -63
  204. package/templates/skills/assistkick-interview/SKILL.md +26 -26
  205. package/templates/skills/assistkick-video-composition-agent/SKILL.md +231 -0
  206. package/templates/skills/assistkick-video-script-writer/SKILL.md +136 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * HighlightedText — splits text around case-insensitive search matches
3
+ * and wraps each match in a <mark> tag for visual highlighting.
4
+ *
5
+ * When searchQuery is empty or absent, renders children as-is.
6
+ */
7
+
8
+ import type { ReactNode } from 'react';
9
+
10
+ interface HighlightedTextProps {
11
+ text: string;
12
+ searchQuery: string;
13
+ /** Index of the currently focused match (0-based global index across all messages). -1 = none. */
14
+ activeMatchIndex?: number;
15
+ /** The starting global index offset for matches within this text. */
16
+ matchIndexOffset?: number;
17
+ }
18
+
19
+ export function HighlightedText({
20
+ text,
21
+ searchQuery,
22
+ activeMatchIndex = -1,
23
+ matchIndexOffset = 0,
24
+ }: HighlightedTextProps) {
25
+ if (!searchQuery) return <>{text}</>;
26
+
27
+ const query = searchQuery.toLowerCase();
28
+ const parts: ReactNode[] = [];
29
+ let remaining = text;
30
+ let localIdx = 0;
31
+
32
+ while (remaining.length > 0) {
33
+ const lowerRemaining = remaining.toLowerCase();
34
+ const matchPos = lowerRemaining.indexOf(query);
35
+
36
+ if (matchPos === -1) {
37
+ parts.push(remaining);
38
+ break;
39
+ }
40
+
41
+ // Text before the match
42
+ if (matchPos > 0) {
43
+ parts.push(remaining.slice(0, matchPos));
44
+ }
45
+
46
+ const matchedText = remaining.slice(matchPos, matchPos + searchQuery.length);
47
+ const globalIdx = matchIndexOffset + localIdx;
48
+ const isActive = globalIdx === activeMatchIndex;
49
+
50
+ parts.push(
51
+ <mark
52
+ key={`${matchIndexOffset}-${localIdx}`}
53
+ className={
54
+ isActive
55
+ ? 'bg-accent text-surface rounded-sm px-[1px]'
56
+ : 'bg-accent/30 text-content rounded-sm px-[1px]'
57
+ }
58
+ data-search-match={globalIdx}
59
+ >
60
+ {matchedText}
61
+ </mark>,
62
+ );
63
+
64
+ remaining = remaining.slice(matchPos + searchQuery.length);
65
+ localIdx++;
66
+ }
67
+
68
+ return <>{parts}</>;
69
+ }
70
+
71
+ /**
72
+ * Count the number of case-insensitive occurrences of `query` in `text`.
73
+ */
74
+ export function countMatches(text: string, query: string): number {
75
+ if (!query) return 0;
76
+ const lower = text.toLowerCase();
77
+ const q = query.toLowerCase();
78
+ let count = 0;
79
+ let pos = 0;
80
+ while (true) {
81
+ const idx = lower.indexOf(q, pos);
82
+ if (idx === -1) break;
83
+ count++;
84
+ pos = idx + q.length;
85
+ }
86
+ return count;
87
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * ImageLightbox — fullscreen overlay for viewing chat images.
3
+ *
4
+ * Features:
5
+ * - Fullscreen dark backdrop with the image centered
6
+ * - Prev/next navigation when multiple images are present
7
+ * - Zoom via mouse wheel, pan by dragging when zoomed
8
+ * - Closes on Escape, clicking the backdrop, or the close button
9
+ * - Keyboard navigation: Arrow Left/Right for prev/next
10
+ */
11
+
12
+ import { useState, useCallback, useEffect, useRef } from 'react';
13
+ import { createPortal } from 'react-dom';
14
+ import { X, ChevronLeft, ChevronRight } from 'lucide-react';
15
+
16
+ export interface LightboxImage {
17
+ src: string;
18
+ alt: string;
19
+ }
20
+
21
+ interface ImageLightboxProps {
22
+ images: LightboxImage[];
23
+ initialIndex: number;
24
+ onClose: () => void;
25
+ }
26
+
27
+ const MIN_ZOOM = 1;
28
+ const MAX_ZOOM = 5;
29
+ const ZOOM_STEP = 0.3;
30
+
31
+ export function ImageLightbox({ images, initialIndex, onClose }: ImageLightboxProps) {
32
+ const [currentIndex, setCurrentIndex] = useState(initialIndex);
33
+ const [zoom, setZoom] = useState(1);
34
+ const [pan, setPan] = useState({ x: 0, y: 0 });
35
+ const [dragging, setDragging] = useState(false);
36
+ const dragStart = useRef({ x: 0, y: 0 });
37
+ const panStart = useRef({ x: 0, y: 0 });
38
+
39
+ const hasMultiple = images.length > 1;
40
+
41
+ const resetTransform = useCallback(() => {
42
+ setZoom(1);
43
+ setPan({ x: 0, y: 0 });
44
+ }, []);
45
+
46
+ const goNext = useCallback(() => {
47
+ if (!hasMultiple) return;
48
+ resetTransform();
49
+ setCurrentIndex((prev) => (prev + 1) % images.length);
50
+ }, [hasMultiple, images.length, resetTransform]);
51
+
52
+ const goPrev = useCallback(() => {
53
+ if (!hasMultiple) return;
54
+ resetTransform();
55
+ setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
56
+ }, [hasMultiple, images.length, resetTransform]);
57
+
58
+ // Keyboard handling
59
+ useEffect(() => {
60
+ const handler = (e: KeyboardEvent) => {
61
+ if (e.key === 'Escape') {
62
+ onClose();
63
+ } else if (e.key === 'ArrowRight') {
64
+ goNext();
65
+ } else if (e.key === 'ArrowLeft') {
66
+ goPrev();
67
+ }
68
+ };
69
+ window.addEventListener('keydown', handler);
70
+ return () => window.removeEventListener('keydown', handler);
71
+ }, [onClose, goNext, goPrev]);
72
+
73
+ // Mouse wheel zoom
74
+ const handleWheel = useCallback((e: React.WheelEvent) => {
75
+ e.preventDefault();
76
+ setZoom((prev) => {
77
+ const next = prev + (e.deltaY < 0 ? ZOOM_STEP : -ZOOM_STEP);
78
+ const clamped = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, next));
79
+ if (clamped === 1) setPan({ x: 0, y: 0 });
80
+ return clamped;
81
+ });
82
+ }, []);
83
+
84
+ // Pan handling
85
+ const handleMouseDown = useCallback((e: React.MouseEvent) => {
86
+ if (zoom <= 1) return;
87
+ e.preventDefault();
88
+ setDragging(true);
89
+ dragStart.current = { x: e.clientX, y: e.clientY };
90
+ panStart.current = { ...pan };
91
+ }, [zoom, pan]);
92
+
93
+ const handleMouseMove = useCallback((e: React.MouseEvent) => {
94
+ if (!dragging) return;
95
+ setPan({
96
+ x: panStart.current.x + (e.clientX - dragStart.current.x),
97
+ y: panStart.current.y + (e.clientY - dragStart.current.y),
98
+ });
99
+ }, [dragging]);
100
+
101
+ const handleMouseUp = useCallback(() => {
102
+ setDragging(false);
103
+ }, []);
104
+
105
+ // Click on backdrop closes
106
+ const handleBackdropClick = useCallback((e: React.MouseEvent) => {
107
+ if (e.target === e.currentTarget) {
108
+ onClose();
109
+ }
110
+ }, [onClose]);
111
+
112
+ const current = images[currentIndex];
113
+ if (!current) return null;
114
+
115
+ const content = (
116
+ <div
117
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/90"
118
+ onClick={handleBackdropClick}
119
+ onMouseMove={handleMouseMove}
120
+ onMouseUp={handleMouseUp}
121
+ onMouseLeave={handleMouseUp}
122
+ role="dialog"
123
+ aria-label="Image lightbox"
124
+ >
125
+ {/* Close button */}
126
+ <button
127
+ type="button"
128
+ className="absolute top-4 right-4 z-10 p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors cursor-pointer border-none"
129
+ onClick={onClose}
130
+ aria-label="Close lightbox"
131
+ >
132
+ <X size={20} />
133
+ </button>
134
+
135
+ {/* Image counter */}
136
+ {hasMultiple && (
137
+ <span className="absolute top-4 left-1/2 -translate-x-1/2 z-10 text-[13px] font-mono text-white/70 bg-black/50 px-3 py-1 rounded-full">
138
+ {currentIndex + 1} / {images.length}
139
+ </span>
140
+ )}
141
+
142
+ {/* Previous button */}
143
+ {hasMultiple && (
144
+ <button
145
+ type="button"
146
+ className="absolute left-4 top-1/2 -translate-y-1/2 z-10 p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors cursor-pointer border-none"
147
+ onClick={goPrev}
148
+ aria-label="Previous image"
149
+ >
150
+ <ChevronLeft size={24} />
151
+ </button>
152
+ )}
153
+
154
+ {/* Next button */}
155
+ {hasMultiple && (
156
+ <button
157
+ type="button"
158
+ className="absolute right-4 top-1/2 -translate-y-1/2 z-10 p-2 rounded-full bg-black/50 hover:bg-black/70 text-white transition-colors cursor-pointer border-none"
159
+ onClick={goNext}
160
+ aria-label="Next image"
161
+ >
162
+ <ChevronRight size={24} />
163
+ </button>
164
+ )}
165
+
166
+ {/* Image */}
167
+ <div
168
+ className="max-w-[90vw] max-h-[90vh] overflow-hidden select-none"
169
+ onWheel={handleWheel}
170
+ onMouseDown={handleMouseDown}
171
+ style={{ cursor: zoom > 1 ? (dragging ? 'grabbing' : 'grab') : 'default' }}
172
+ >
173
+ <img
174
+ src={current.src}
175
+ alt={current.alt}
176
+ className="max-w-[90vw] max-h-[90vh] object-contain transition-transform duration-100"
177
+ style={{
178
+ transform: `scale(${zoom}) translate(${pan.x / zoom}px, ${pan.y / zoom}px)`,
179
+ }}
180
+ draggable={false}
181
+ />
182
+ </div>
183
+
184
+ {/* Image name */}
185
+ <span className="absolute bottom-4 left-1/2 -translate-x-1/2 z-10 text-[12px] font-mono text-white/60 bg-black/50 px-3 py-1 rounded-full max-w-[60vw] truncate">
186
+ {current.alt}
187
+ </span>
188
+ </div>
189
+ );
190
+
191
+ return createPortal(content, document.body);
192
+ }
@@ -225,11 +225,11 @@ export function KanbanView({ graphData, projectId, onCardClick, onIssuesClick }:
225
225
  setDraggedCard(featureId);
226
226
  e.dataTransfer.effectAllowed = 'move';
227
227
  e.dataTransfer.setData('text/plain', featureId);
228
- (e.target as HTMLElement).classList.add('dragging');
228
+ (e.target as HTMLElement).classList.add('opacity-40');
229
229
  };
230
230
 
231
231
  const handleDragEnd = (e: React.DragEvent) => {
232
- (e.target as HTMLElement).classList.remove('dragging');
232
+ (e.target as HTMLElement).classList.remove('opacity-40');
233
233
  setDraggedCard(null);
234
234
  setDragOverColumn(null);
235
235
  };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * MentionPill — renders a detected @file or /skill mention as a removable pill.
3
+ * Shows an icon, the mention value, and an X button for removal.
4
+ */
5
+
6
+ import { X, FileText, Terminal } from 'lucide-react';
7
+ import type { Mention } from '../hooks/use_mention_autocomplete';
8
+
9
+ interface MentionPillProps {
10
+ mention: Mention;
11
+ onRemove: (mention: Mention) => void;
12
+ }
13
+
14
+ export const MentionPill = ({ mention, onRemove }: MentionPillProps) => {
15
+ return (
16
+ <span className="inline-flex items-center gap-1 px-2 py-0.5 bg-accent/10 border border-accent/20 rounded text-[11px] font-mono text-accent">
17
+ {mention.type === 'file' ? (
18
+ <FileText size={10} className="shrink-0" />
19
+ ) : (
20
+ <Terminal size={10} className="shrink-0" />
21
+ )}
22
+ <span className="truncate max-w-[200px]">{mention.displayValue}</span>
23
+ <button
24
+ type="button"
25
+ className="ml-0.5 text-accent/60 hover:text-accent cursor-pointer bg-transparent border-none p-0 leading-none"
26
+ onClick={() => onRemove(mention)}
27
+ tabIndex={-1}
28
+ >
29
+ <X size={10} />
30
+ </button>
31
+ </span>
32
+ );
33
+ };
@@ -0,0 +1,148 @@
1
+ /**
2
+ * MermaidBlock — renders a mermaid code block as an inline SVG diagram.
3
+ *
4
+ * Lazily loads the mermaid library on first render. Respects dark/light theme.
5
+ * Falls back to raw source with error indicator on syntax errors.
6
+ * Provides an enlarge button to open the diagram fullscreen.
7
+ */
8
+
9
+ import { useState, useEffect, useRef, useCallback } from 'react';
10
+ import { Maximize2, AlertTriangle } from 'lucide-react';
11
+ import { DiagramOverlay } from './DiagramOverlay';
12
+ import { resolveMermaidTheme, getDesignSystemThemeVariables } from '../lib/mermaid_helpers';
13
+
14
+ type MermaidApi = typeof import('mermaid').default;
15
+
16
+ let mermaidInstance: MermaidApi | null = null;
17
+ let mermaidLoadPromise: Promise<MermaidApi> | null = null;
18
+
19
+ const loadMermaid = (): Promise<MermaidApi> => {
20
+ if (mermaidInstance) return Promise.resolve(mermaidInstance);
21
+ if (mermaidLoadPromise) return mermaidLoadPromise;
22
+
23
+ mermaidLoadPromise = import('mermaid').then((mod) => {
24
+ mermaidInstance = mod.default;
25
+ mermaidInstance.initialize({ startOnLoad: false, securityLevel: 'strict' });
26
+ return mermaidInstance;
27
+ });
28
+
29
+ return mermaidLoadPromise;
30
+ };
31
+
32
+ let renderCounter = 0;
33
+
34
+ interface MermaidBlockProps {
35
+ code: string;
36
+ }
37
+
38
+ export const MermaidBlock = ({ code }: MermaidBlockProps) => {
39
+ const [svgHtml, setSvgHtml] = useState<string | null>(null);
40
+ const [error, setError] = useState<string | null>(null);
41
+ const [loading, setLoading] = useState(true);
42
+ const [overlayOpen, setOverlayOpen] = useState(false);
43
+ const containerRef = useRef<HTMLDivElement>(null);
44
+
45
+ const renderDiagram = useCallback(async () => {
46
+ setLoading(true);
47
+ setError(null);
48
+ setSvgHtml(null);
49
+
50
+ try {
51
+ const mermaid = await loadMermaid();
52
+ const theme = resolveMermaidTheme(document.documentElement.getAttribute('data-theme'));
53
+
54
+ mermaid.initialize({
55
+ startOnLoad: false,
56
+ securityLevel: 'strict',
57
+ theme,
58
+ themeVariables: getDesignSystemThemeVariables(),
59
+ flowchart: { padding: 16, nodeSpacing: 60, rankSpacing: 60 },
60
+ });
61
+
62
+ const id = `mermaid-${Date.now()}-${renderCounter++}`;
63
+ const { svg } = await mermaid.render(id, code.trim());
64
+ setSvgHtml(svg);
65
+ } catch (err) {
66
+ const message = err instanceof Error ? err.message : 'Failed to render diagram';
67
+ setError(message);
68
+ } finally {
69
+ setLoading(false);
70
+ }
71
+ }, [code]);
72
+
73
+ useEffect(() => {
74
+ renderDiagram();
75
+ }, [renderDiagram]);
76
+
77
+ // Re-render on theme change
78
+ useEffect(() => {
79
+ const observer = new MutationObserver((mutations) => {
80
+ for (const mutation of mutations) {
81
+ if (mutation.attributeName === 'data-theme') {
82
+ renderDiagram();
83
+ break;
84
+ }
85
+ }
86
+ });
87
+
88
+ observer.observe(document.documentElement, { attributes: true });
89
+ return () => observer.disconnect();
90
+ }, [renderDiagram]);
91
+
92
+ const openOverlay = useCallback(() => {
93
+ setOverlayOpen(true);
94
+ }, []);
95
+
96
+ const closeOverlay = useCallback(() => {
97
+ setOverlayOpen(false);
98
+ }, []);
99
+
100
+ if (loading) {
101
+ return (
102
+ <div className="flex items-center justify-center py-6 px-4 rounded-md bg-surface-raised text-content-secondary text-xs font-mono">
103
+ Rendering diagram…
104
+ </div>
105
+ );
106
+ }
107
+
108
+ if (error) {
109
+ return (
110
+ <div className="rounded-md border border-edge overflow-hidden">
111
+ <div className="flex items-center gap-1.5 px-3 py-1.5 bg-surface-raised text-xs text-amber-400 border-b border-edge">
112
+ <AlertTriangle size={13} />
113
+ <span>Mermaid rendering error</span>
114
+ </div>
115
+ <pre className="bg-surface-raised py-2.5 px-3 overflow-x-auto text-xs leading-normal font-mono text-content m-0">
116
+ {code}
117
+ </pre>
118
+ </div>
119
+ );
120
+ }
121
+
122
+ return (
123
+ <>
124
+ <div
125
+ ref={containerRef}
126
+ className="mermaid-diagram relative group rounded-md border border-edge overflow-hidden bg-surface-raised"
127
+ >
128
+ <div
129
+ className="p-5 overflow-x-auto [&_svg]:max-w-full [&_svg]:h-auto"
130
+ dangerouslySetInnerHTML={{ __html: svgHtml! }}
131
+ />
132
+ <button
133
+ type="button"
134
+ onClick={openOverlay}
135
+ className="absolute top-2 right-2 p-1.5 rounded bg-surface/80 border border-edge text-content-secondary
136
+ opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer hover:text-content hover:bg-surface"
137
+ aria-label="Enlarge diagram"
138
+ >
139
+ <Maximize2 size={14} />
140
+ </button>
141
+ </div>
142
+
143
+ {overlayOpen && svgHtml && (
144
+ <DiagramOverlay svgHtml={svgHtml} onClose={closeOverlay} />
145
+ )}
146
+ </>
147
+ );
148
+ };
@@ -0,0 +1,91 @@
1
+ /**
2
+ * PermissionDialog — modal shown when Claude CLI requests permission to use a tool.
3
+ * Offers three choices: Allow once, Allow for this session, Allow always.
4
+ * Also offers a Deny option.
5
+ */
6
+
7
+ import React, { useCallback } from 'react';
8
+
9
+ export type PermissionDecision = 'allow_once' | 'allow_session' | 'allow_always' | 'deny';
10
+
11
+ export interface PermissionRequestData {
12
+ requestId: string;
13
+ toolName: string;
14
+ input: Record<string, unknown>;
15
+ }
16
+
17
+ interface PermissionDialogProps {
18
+ request: PermissionRequestData | null;
19
+ onRespond: (requestId: string, decision: PermissionDecision) => void;
20
+ }
21
+
22
+ export function PermissionDialog({ request, onRespond }: PermissionDialogProps) {
23
+ const handleDecision = useCallback((decision: PermissionDecision) => {
24
+ if (!request) return;
25
+ onRespond(request.requestId, decision);
26
+ }, [request, onRespond]);
27
+
28
+ if (!request) return null;
29
+
30
+ const inputSummary = Object.keys(request.input).length > 0
31
+ ? JSON.stringify(request.input, null, 2)
32
+ : null;
33
+
34
+ return (
35
+ <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-[1000]">
36
+ <div
37
+ className="bg-surface border border-edge rounded-xl p-6 w-[480px] max-w-[90vw] shadow-[0_8px_32px_rgba(0,0,0,0.3)]"
38
+ onClick={e => e.stopPropagation()}
39
+ >
40
+ <div className="flex items-center gap-2 mb-4">
41
+ <div className="w-2 h-2 rounded-full bg-[#f5a623] animate-pulse" />
42
+ <h2 className="m-0 text-lg text-content font-mono">Permission Required</h2>
43
+ </div>
44
+
45
+ <p className="text-sm text-content-secondary mb-3 font-mono">
46
+ Claude wants to use the following tool:
47
+ </p>
48
+
49
+ <div className="bg-surface-raised border border-edge rounded-lg px-4 py-3 mb-4">
50
+ <div className="font-mono text-sm text-accent font-semibold">{request.toolName}</div>
51
+ {inputSummary && (
52
+ <pre className="mt-2 text-xs text-content-secondary font-mono whitespace-pre-wrap break-words max-h-[200px] overflow-y-auto m-0">
53
+ {inputSummary}
54
+ </pre>
55
+ )}
56
+ </div>
57
+
58
+ <div className="flex flex-col gap-2">
59
+ <button
60
+ className="w-full h-9 bg-transparent border border-accent rounded text-accent font-mono text-[13px] cursor-pointer transition-[background,color] duration-150 hover:bg-accent hover:text-white"
61
+ onClick={() => handleDecision('allow_once')}
62
+ type="button"
63
+ >
64
+ Allow once
65
+ </button>
66
+ <button
67
+ className="w-full h-9 bg-transparent border border-accent rounded text-accent font-mono text-[13px] cursor-pointer transition-[background,color] duration-150 hover:bg-accent hover:text-white"
68
+ onClick={() => handleDecision('allow_session')}
69
+ type="button"
70
+ >
71
+ Allow for this session
72
+ </button>
73
+ <button
74
+ className="w-full h-9 bg-transparent border border-accent rounded text-accent font-mono text-[13px] cursor-pointer transition-[background,color] duration-150 hover:bg-accent hover:text-white"
75
+ onClick={() => handleDecision('allow_always')}
76
+ type="button"
77
+ >
78
+ Allow always for this tool
79
+ </button>
80
+ <button
81
+ className="w-full h-9 bg-transparent border border-error rounded text-error font-mono text-[13px] cursor-pointer transition-[background,color] duration-150 hover:bg-error hover:text-white mt-1"
82
+ onClick={() => handleDecision('deny')}
83
+ type="button"
84
+ >
85
+ Deny
86
+ </button>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ );
91
+ }