@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.
- package/package.json +2 -2
- package/templates/assistkick-product-system/.env.example +1 -0
- package/templates/assistkick-product-system/local.db +0 -0
- package/templates/assistkick-product-system/package.json +4 -2
- package/templates/assistkick-product-system/packages/backend/package.json +2 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
- package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +43 -4
- package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
- package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
- package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
- package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +154 -0
- package/templates/assistkick-product-system/packages/backend/src/server.ts +81 -9
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
- package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
- package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
- package/templates/assistkick-product-system/packages/backend/src/services/init.ts +28 -78
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
- package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
- package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
- package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
- package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
- package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +222 -0
- package/templates/assistkick-product-system/packages/frontend/index.html +3 -0
- package/templates/assistkick-product-system/packages/frontend/package-lock.json +800 -11
- package/templates/assistkick-product-system/packages/frontend/package.json +11 -1
- package/templates/assistkick-product-system/packages/frontend/src/App.tsx +24 -7
- package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +456 -16
- package/templates/assistkick-product-system/packages/frontend/src/api/client_files.test.ts +172 -0
- package/templates/assistkick-product-system/packages/frontend/src/api/client_video.test.ts +238 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +307 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/CoherenceView.tsx +82 -66
- package/templates/assistkick-product-system/packages/frontend/src/components/CompositionPlaceholder.tsx +97 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/DesignSystemView.tsx +383 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +57 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +313 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeContextMenu.tsx +61 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +73 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +404 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +193 -64
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +71 -73
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphSettings.tsx +8 -8
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphView.tsx +1 -1
- package/templates/assistkick-product-system/packages/frontend/src/components/InviteUserDialog.tsx +15 -11
- package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +226 -291
- package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
- package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
- package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +40 -66
- package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +55 -115
- package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
- package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +155 -77
- package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
- package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +270 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +37 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +207 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +335 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +634 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +208 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +107 -0
- package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useGraph.ts +6 -21
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +15 -80
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
- package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
- package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
- package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
- package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
- package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +19 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +54 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +93 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +30 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +9 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useGitModalStore.ts +14 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphStore.ts +36 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphUIStore.ts +25 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +90 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useQaSheetStore.ts +27 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +76 -0
- package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +336 -3632
- package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
- package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
- package/templates/assistkick-product-system/packages/frontend/vite.config.ts +7 -1
- package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
- package/templates/assistkick-product-system/packages/shared/db/schema.ts +113 -0
- package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
- package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
- package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
- package/templates/assistkick-product-system/packages/shared/lib/graph.ts +16 -5
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1753 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1281 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
- package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
- package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
- package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
- package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
- package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
- package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
- package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
- package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
- package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
- package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
- package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
- package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
- package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
- package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
- package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
- package/templates/assistkick-product-system/packages/video/index.ts +4 -0
- package/templates/assistkick-product-system/packages/video/package.json +28 -0
- package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
- package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
- package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
- package/templates/assistkick-product-system/packages/video/style.css +1 -0
- package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
- package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
- package/templates/assistkick-product-system/tests/video_render_service.test.ts +179 -0
- package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
- package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
- package/templates/skills/assistkick-bootstrap/SKILL.md +3 -3
- package/templates/skills/assistkick-code-reviewer/SKILL.md +2 -2
- package/templates/skills/assistkick-debugger/SKILL.md +2 -2
- package/templates/skills/assistkick-developer/SKILL.md +6 -3
- package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
- package/templates/skills/assistkick-interview/SKILL.md +2 -2
- package/templates/skills/product-system/graph.json +1890 -0
- package/templates/skills/product-system/kanban.json +304 -0
- package/templates/skills/product-system/nodes/comp_001.md +56 -0
- package/templates/skills/product-system/nodes/comp_002.md +57 -0
- package/templates/skills/product-system/nodes/data_001.md +51 -0
- package/templates/skills/product-system/nodes/data_002.md +40 -0
- package/templates/skills/product-system/nodes/data_004.md +38 -0
- package/templates/skills/product-system/nodes/dec_001.md +34 -0
- package/templates/skills/product-system/nodes/dec_016.md +32 -0
- package/templates/skills/product-system/nodes/feat_008.md +30 -0
- package/templates/skills/video-composition-agent/SKILL.md +232 -0
- package/templates/skills/video-script-writer/SKILL.md +136 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AbsoluteFill, useCurrentFrame, useVideoConfig, interpolate } from 'remotion';
|
|
3
|
+
|
|
4
|
+
interface CompositionPlaceholderProps {
|
|
5
|
+
compositionId: string;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A placeholder composition component rendered by the Remotion Player
|
|
12
|
+
* when the actual composition bundle hasn't been loaded yet.
|
|
13
|
+
*
|
|
14
|
+
* Shows the composition ID, dimensions, and a simple animated progress bar
|
|
15
|
+
* to verify the player timeline and scrubbing work correctly.
|
|
16
|
+
*/
|
|
17
|
+
export const CompositionPlaceholder: React.FC<CompositionPlaceholderProps> = ({
|
|
18
|
+
compositionId,
|
|
19
|
+
width,
|
|
20
|
+
height,
|
|
21
|
+
}) => {
|
|
22
|
+
const frame = useCurrentFrame();
|
|
23
|
+
const { fps, durationInFrames } = useVideoConfig();
|
|
24
|
+
const progress = frame / durationInFrames;
|
|
25
|
+
const seconds = (frame / fps).toFixed(1);
|
|
26
|
+
|
|
27
|
+
const opacity = interpolate(frame, [0, 15], [0, 1], {
|
|
28
|
+
extrapolateRight: 'clamp',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<AbsoluteFill
|
|
33
|
+
style={{
|
|
34
|
+
backgroundColor: '#0e100e',
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'column',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
justifyContent: 'center',
|
|
39
|
+
fontFamily: 'system-ui, sans-serif',
|
|
40
|
+
color: '#bbdedd',
|
|
41
|
+
opacity,
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<div
|
|
45
|
+
style={{
|
|
46
|
+
fontSize: 14,
|
|
47
|
+
letterSpacing: '0.1em',
|
|
48
|
+
textTransform: 'uppercase',
|
|
49
|
+
marginBottom: 16,
|
|
50
|
+
color: '#6b7280',
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
Composition Preview
|
|
54
|
+
</div>
|
|
55
|
+
<div
|
|
56
|
+
style={{
|
|
57
|
+
fontSize: 28,
|
|
58
|
+
fontWeight: 600,
|
|
59
|
+
marginBottom: 8,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
{compositionId}
|
|
63
|
+
</div>
|
|
64
|
+
<div
|
|
65
|
+
style={{
|
|
66
|
+
fontSize: 13,
|
|
67
|
+
color: '#6b7280',
|
|
68
|
+
marginBottom: 32,
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
{width}x{height} · {fps}fps · {seconds}s / {(durationInFrames / fps).toFixed(1)}s
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Progress bar */}
|
|
75
|
+
<div
|
|
76
|
+
style={{
|
|
77
|
+
width: '60%',
|
|
78
|
+
maxWidth: 400,
|
|
79
|
+
height: 4,
|
|
80
|
+
borderRadius: 2,
|
|
81
|
+
backgroundColor: '#1f2937',
|
|
82
|
+
overflow: 'hidden',
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<div
|
|
86
|
+
style={{
|
|
87
|
+
width: `${progress * 100}%`,
|
|
88
|
+
height: '100%',
|
|
89
|
+
borderRadius: 2,
|
|
90
|
+
backgroundColor: '#bbdedd',
|
|
91
|
+
transition: 'none',
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
</AbsoluteFill>
|
|
96
|
+
);
|
|
97
|
+
};
|
package/templates/assistkick-product-system/packages/frontend/src/components/DesignSystemView.tsx
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Columns3, Clock, FileText, Search, Settings } from 'lucide-react';
|
|
3
|
+
import { NavBarSidekick } from './ds/NavBarSidekick';
|
|
4
|
+
import type { NavItem as SidekickNavItem } from './ds/NavBarSidekick';
|
|
5
|
+
import { KanbanCardShowcase } from './ds/KanbanCardShowcase';
|
|
6
|
+
import { ButtonShowcase } from './ds/ButtonShowcase';
|
|
7
|
+
import { SidePanelShowcase } from './ds/SidePanelShowcase';
|
|
8
|
+
|
|
9
|
+
interface DesignSystemViewProps {
|
|
10
|
+
visible: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* ── Collapsible card section ── */
|
|
14
|
+
|
|
15
|
+
function Section({ title, defaultOpen = false, children }: {
|
|
16
|
+
title: string;
|
|
17
|
+
defaultOpen?: boolean;
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}) {
|
|
20
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<section className="mb-5">
|
|
24
|
+
<button
|
|
25
|
+
onClick={() => setOpen(v => !v)}
|
|
26
|
+
className={[
|
|
27
|
+
'flex w-full items-center justify-between',
|
|
28
|
+
'rounded-2xl px-6 py-4',
|
|
29
|
+
'bg-surface-alt border border-edge',
|
|
30
|
+
'cursor-pointer text-left outline-none',
|
|
31
|
+
'transition-colors duration-150 hover:border-content/15',
|
|
32
|
+
].join(' ')}
|
|
33
|
+
>
|
|
34
|
+
<span className="text-[13px] font-bold uppercase tracking-widest text-content">
|
|
35
|
+
{title}
|
|
36
|
+
</span>
|
|
37
|
+
<span
|
|
38
|
+
className="text-[12px] text-content-muted transition-transform duration-200"
|
|
39
|
+
style={{ transform: open ? 'rotate(180deg)' : 'rotate(0deg)' }}
|
|
40
|
+
>
|
|
41
|
+
▾
|
|
42
|
+
</span>
|
|
43
|
+
</button>
|
|
44
|
+
{open && (
|
|
45
|
+
<div className="mt-3 rounded-2xl border border-edge bg-surface-alt p-6">
|
|
46
|
+
{children}
|
|
47
|
+
</div>
|
|
48
|
+
)}
|
|
49
|
+
</section>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* ── Subsection label ── */
|
|
54
|
+
|
|
55
|
+
function SubLabel({ children }: { children: React.ReactNode }) {
|
|
56
|
+
return (
|
|
57
|
+
<h4 className="mb-3 mt-8 text-[11px] font-bold uppercase tracking-widest text-content-muted">
|
|
58
|
+
{children}
|
|
59
|
+
</h4>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ── Token data ── */
|
|
64
|
+
|
|
65
|
+
const COLOR_TOKENS = [
|
|
66
|
+
{ name: '--bg-primary', token: 'surface', label: 'Surface', desc: 'Page background' },
|
|
67
|
+
{ name: '--bg-secondary', token: 'surface-alt', label: 'Surface Alt', desc: 'Card background' },
|
|
68
|
+
{ name: '--bg-tertiary', token: 'surface-raised', label: 'Surface Raised', desc: 'Raised elements' },
|
|
69
|
+
{ name: '--border-color', token: 'edge', label: 'Edge', desc: 'Borders & dividers' },
|
|
70
|
+
{ name: '--text-primary', token: 'content', label: 'Content', desc: 'Primary text' },
|
|
71
|
+
{ name: '--text-secondary', token: 'content-secondary', label: 'Content 2nd', desc: 'Secondary text' },
|
|
72
|
+
{ name: '--text-muted', token: 'content-muted', label: 'Muted', desc: 'Placeholder text' },
|
|
73
|
+
{ name: '--accent', token: 'accent', label: 'Accent', desc: 'Primary — green' },
|
|
74
|
+
{ name: '--accent-secondary', token: 'accent-secondary', label: 'Accent 2nd', desc: 'Secondary — orange' },
|
|
75
|
+
{ name: '--error-color', token: 'error', label: 'Error', desc: 'Error states' },
|
|
76
|
+
{ name: '--panel-bg', token: 'panel', label: 'Panel', desc: 'Side panel bg' },
|
|
77
|
+
{ name: '--completeness-bg', token: 'completeness-bg', label: 'Progress BG', desc: 'Track' },
|
|
78
|
+
{ name: '--completeness-fill', token: 'completeness-fill', label: 'Progress Fill', desc: 'Fill' },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const TYPOGRAPHY_SCALE = [
|
|
82
|
+
{ name: '3xl', size: '28px', weight: '700', element: 'Page title' },
|
|
83
|
+
{ name: '2xl', size: '22px', weight: '600', element: 'Section heading' },
|
|
84
|
+
{ name: 'xl', size: '18px', weight: '600', element: 'Subsection' },
|
|
85
|
+
{ name: 'lg', size: '16px', weight: '500', element: 'Large body' },
|
|
86
|
+
{ name: 'base', size: '14px', weight: '400', element: 'Body text' },
|
|
87
|
+
{ name: 'sm', size: '13px', weight: '400', element: 'Small text' },
|
|
88
|
+
{ name: 'xs', size: '12px', weight: '400', element: 'Caption' },
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const SPACING_SCALE = [
|
|
92
|
+
{ name: '1', px: 4 },
|
|
93
|
+
{ name: '2', px: 8 },
|
|
94
|
+
{ name: '3', px: 12 },
|
|
95
|
+
{ name: '4', px: 16 },
|
|
96
|
+
{ name: '6', px: 24 },
|
|
97
|
+
{ name: '8', px: 32 },
|
|
98
|
+
{ name: '10', px: 40 },
|
|
99
|
+
{ name: '12', px: 48 },
|
|
100
|
+
{ name: '16', px: 64 },
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
/* ── Demo data ── */
|
|
104
|
+
|
|
105
|
+
const DEMO_SIDEKICK_ITEMS: SidekickNavItem[] = [
|
|
106
|
+
{ id: 'board', label: 'Board', icon: <Columns3 size={14} strokeWidth={2} />, description: 'Kanban board view' },
|
|
107
|
+
{ id: 'timeline', label: 'Timeline', icon: <Clock size={14} strokeWidth={2} />, description: 'Gantt chart & milestones' },
|
|
108
|
+
{ id: 'docs', label: 'Docs', icon: <FileText size={14} strokeWidth={2} />, description: 'Documentation & notes' },
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
/* ── Main ── */
|
|
112
|
+
|
|
113
|
+
export function DesignSystemView({ visible }: DesignSystemViewProps) {
|
|
114
|
+
if (!visible) return null;
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div className="absolute inset-0 overflow-y-auto bg-surface text-content">
|
|
118
|
+
<div className="px-10 py-14">
|
|
119
|
+
|
|
120
|
+
{/* Page Header */}
|
|
121
|
+
<div className="mb-10">
|
|
122
|
+
<h1 className="text-[32px] font-bold uppercase tracking-tight text-content">
|
|
123
|
+
Design System
|
|
124
|
+
</h1>
|
|
125
|
+
<p className="mt-2 text-[14px] leading-relaxed text-content-secondary">
|
|
126
|
+
Living style guide. Tailwind CSS + CSS custom properties. Toggle theme to see tokens adapt.
|
|
127
|
+
</p>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
{/* ── NavBar Segmented ── */}
|
|
131
|
+
<Section title="Navigation Bar" defaultOpen>
|
|
132
|
+
<p className="mb-5 text-[13px] text-content-muted">
|
|
133
|
+
Segmented control with filled active state and an integrated command palette search.
|
|
134
|
+
Active item gets an accent fill with glow. Press '/' or click to open command palette.
|
|
135
|
+
</p>
|
|
136
|
+
|
|
137
|
+
<div className="rounded-2xl border border-edge overflow-hidden">
|
|
138
|
+
<NavBarSidekick
|
|
139
|
+
items={DEMO_SIDEKICK_ITEMS}
|
|
140
|
+
brand={
|
|
141
|
+
<div className="flex items-center gap-2">
|
|
142
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-lg border border-accent/30 font-bold text-accent">
|
|
143
|
+
<Columns3 size={16} strokeWidth={2} />
|
|
144
|
+
</div>
|
|
145
|
+
<div>
|
|
146
|
+
<div className="text-[12px] font-semibold text-content leading-none">Workspace</div>
|
|
147
|
+
<div className="text-[10px] text-content-muted">Pro plan</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
}
|
|
151
|
+
actions={
|
|
152
|
+
<div className="flex items-center gap-1.5">
|
|
153
|
+
<button className="flex h-8 w-8 items-center justify-center rounded-lg border border-edge text-content-muted hover:border-content/20 hover:text-content cursor-pointer outline-none transition-colors">
|
|
154
|
+
<Settings size={14} strokeWidth={2} />
|
|
155
|
+
</button>
|
|
156
|
+
<div className="h-5 w-px bg-edge mx-1" />
|
|
157
|
+
</div>
|
|
158
|
+
}
|
|
159
|
+
trailing={
|
|
160
|
+
<div className="flex items-center gap-1.5">
|
|
161
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-accent text-[11px] font-bold text-surface">
|
|
162
|
+
VD
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
}
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<SubLabel>Segment States</SubLabel>
|
|
170
|
+
<div className="flex flex-wrap gap-3">
|
|
171
|
+
{[
|
|
172
|
+
{ state: 'Default', label: 'Item', active: false },
|
|
173
|
+
{ state: 'Hover', label: 'Item', active: false, hover: true },
|
|
174
|
+
{ state: 'Active', label: 'Item', active: true },
|
|
175
|
+
].map(({ state, label, active, hover }) => (
|
|
176
|
+
<div key={state} className="rounded-xl border border-edge px-4 py-2.5 text-center min-w-[100px]">
|
|
177
|
+
<div
|
|
178
|
+
className={[
|
|
179
|
+
'inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider',
|
|
180
|
+
active
|
|
181
|
+
? 'bg-accent text-surface'
|
|
182
|
+
: hover
|
|
183
|
+
? 'bg-surface-raised text-content'
|
|
184
|
+
: 'text-content-muted',
|
|
185
|
+
].join(' ')}
|
|
186
|
+
style={active ? { boxShadow: '0 2px 8px -2px var(--accent)' } : {}}
|
|
187
|
+
>
|
|
188
|
+
<Columns3 size={12} strokeWidth={2} />
|
|
189
|
+
{label}
|
|
190
|
+
</div>
|
|
191
|
+
<div className="mt-1.5 text-[10px] text-content-muted">{state}</div>
|
|
192
|
+
</div>
|
|
193
|
+
))}
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<SubLabel>Command Palette</SubLabel>
|
|
197
|
+
<div className="flex flex-wrap gap-3">
|
|
198
|
+
<div className="rounded-xl border border-edge px-4 py-2.5 text-center">
|
|
199
|
+
<div className="flex h-8 items-center gap-2 rounded-lg border border-edge px-3 text-[12px] text-content-muted">
|
|
200
|
+
<Search size={13} strokeWidth={2} />
|
|
201
|
+
Search
|
|
202
|
+
<kbd className="ml-1 rounded border border-edge bg-surface-raised px-1 py-0.5 text-[9px] font-mono text-content-muted">/</kbd>
|
|
203
|
+
</div>
|
|
204
|
+
<div className="mt-1 text-[10px] text-content-muted">Closed</div>
|
|
205
|
+
</div>
|
|
206
|
+
<div className="rounded-xl border border-edge px-4 py-2.5 text-center">
|
|
207
|
+
<div className="flex h-8 items-center gap-2 rounded-lg border border-accent/40 bg-surface px-3 text-[12px] text-content">
|
|
208
|
+
<Search size={13} strokeWidth={2} />
|
|
209
|
+
Search
|
|
210
|
+
<kbd className="ml-1 rounded border border-edge bg-surface-raised px-1 py-0.5 text-[9px] font-mono text-content-muted">/</kbd>
|
|
211
|
+
</div>
|
|
212
|
+
<div className="mt-1 text-[10px] text-content-muted">Open</div>
|
|
213
|
+
</div>
|
|
214
|
+
<div className="rounded-xl border border-edge px-4 py-2.5">
|
|
215
|
+
<div className="rounded-lg border border-edge bg-surface px-3 py-1.5">
|
|
216
|
+
<div className="text-[12px] text-content">doc<span className="animate-pulse text-accent">|</span></div>
|
|
217
|
+
<div className="mt-1.5 flex items-center gap-2 rounded-md bg-accent/10 px-2 py-1 text-[11px] text-accent">
|
|
218
|
+
<FileText size={12} strokeWidth={2} /> Docs
|
|
219
|
+
<span className="text-[10px] text-content-muted">— Documentation & notes</span>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="mt-1 text-[10px] text-content-muted text-center">With results</div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</Section>
|
|
226
|
+
|
|
227
|
+
{/* ── Side Panel ── */}
|
|
228
|
+
<Section title="Side Panel" defaultOpen>
|
|
229
|
+
<p className="mb-5 text-[13px] text-content-muted">
|
|
230
|
+
Card stack layout for the node detail side panel. Collapsible sections for description,
|
|
231
|
+
acceptance criteria, open questions, resolved questions, notes, work summaries, and relationships.
|
|
232
|
+
</p>
|
|
233
|
+
<SidePanelShowcase />
|
|
234
|
+
</Section>
|
|
235
|
+
|
|
236
|
+
{/* ── Kanban Card ── */}
|
|
237
|
+
<Section title="Kanban Card" defaultOpen>
|
|
238
|
+
<p className="mb-5 text-[13px] text-content-muted">
|
|
239
|
+
Glass aesthetic with gradient accent strips, frosted backdrop, and floating pill badges.
|
|
240
|
+
States: idle, active pipeline, interrupted/rejected, and blocked.
|
|
241
|
+
</p>
|
|
242
|
+
<KanbanCardShowcase />
|
|
243
|
+
</Section>
|
|
244
|
+
|
|
245
|
+
{/* ── Buttons ── */}
|
|
246
|
+
<Section title="Buttons" defaultOpen>
|
|
247
|
+
<p className="mb-5 text-[13px] text-content-muted">
|
|
248
|
+
Four button variants: Primary (accent fill with glow), Secondary (outline that fills on hover),
|
|
249
|
+
Ghost (subtle border), and Danger (dashed error border). Three sizes, icon-only, and grouped layouts.
|
|
250
|
+
</p>
|
|
251
|
+
<ButtonShowcase />
|
|
252
|
+
</Section>
|
|
253
|
+
|
|
254
|
+
{/* ── Colors ── */}
|
|
255
|
+
<Section title="Color Palette">
|
|
256
|
+
<p className="mb-5 text-[13px] text-content-muted">
|
|
257
|
+
Theme tokens for surfaces, text, accents, and borders. Adapts to light and dark themes.
|
|
258
|
+
</p>
|
|
259
|
+
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
|
|
260
|
+
{COLOR_TOKENS.map(({ name, token, label, desc }) => (
|
|
261
|
+
<div
|
|
262
|
+
key={name}
|
|
263
|
+
className="overflow-hidden rounded-2xl border border-edge"
|
|
264
|
+
>
|
|
265
|
+
<div
|
|
266
|
+
className="h-16"
|
|
267
|
+
style={{ backgroundColor: `var(${name})` }}
|
|
268
|
+
/>
|
|
269
|
+
<div className="bg-surface-alt px-4 py-3">
|
|
270
|
+
<div className="text-[13px] font-semibold text-content">{label}</div>
|
|
271
|
+
<div className="mt-0.5 font-mono text-[10px] text-accent">{token}</div>
|
|
272
|
+
<div className="mt-0.5 text-[11px] text-content-muted">{desc}</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
))}
|
|
276
|
+
</div>
|
|
277
|
+
</Section>
|
|
278
|
+
|
|
279
|
+
{/* ── Typography ── */}
|
|
280
|
+
<Section title="Typography">
|
|
281
|
+
<p className="mb-5 text-[13px] text-content-muted">
|
|
282
|
+
Inter / system-ui. Generous line-heights for comfortable reading.
|
|
283
|
+
</p>
|
|
284
|
+
|
|
285
|
+
<div className="rounded-2xl border border-edge overflow-hidden">
|
|
286
|
+
{TYPOGRAPHY_SCALE.map(({ name, size, weight, element }, i) => (
|
|
287
|
+
<div
|
|
288
|
+
key={name}
|
|
289
|
+
className={[
|
|
290
|
+
'flex items-baseline gap-6 px-5 py-4',
|
|
291
|
+
i > 0 ? 'border-t border-edge' : '',
|
|
292
|
+
].join(' ')}
|
|
293
|
+
>
|
|
294
|
+
<div className="w-14 shrink-0">
|
|
295
|
+
<div className="font-mono text-[10px] font-semibold text-accent">{name}</div>
|
|
296
|
+
<div className="font-mono text-[10px] text-content-muted">{size}</div>
|
|
297
|
+
</div>
|
|
298
|
+
<div className="w-24 shrink-0 text-[11px] text-content-muted uppercase tracking-wide">
|
|
299
|
+
{element}
|
|
300
|
+
</div>
|
|
301
|
+
<div
|
|
302
|
+
className="min-w-0 flex-1 truncate text-content"
|
|
303
|
+
style={{
|
|
304
|
+
fontSize: size,
|
|
305
|
+
fontWeight: Number(weight),
|
|
306
|
+
fontFamily: 'var(--font-system)',
|
|
307
|
+
lineHeight: 1.5,
|
|
308
|
+
}}
|
|
309
|
+
>
|
|
310
|
+
The quick brown fox
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
))}
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<SubLabel>Weights</SubLabel>
|
|
317
|
+
<div className="flex flex-wrap gap-3">
|
|
318
|
+
{[
|
|
319
|
+
{ label: 'Regular', value: 400 },
|
|
320
|
+
{ label: 'Medium', value: 500 },
|
|
321
|
+
{ label: 'Semibold', value: 600 },
|
|
322
|
+
{ label: 'Bold', value: 700 },
|
|
323
|
+
].map(({ label, value }) => (
|
|
324
|
+
<div
|
|
325
|
+
key={value}
|
|
326
|
+
className="rounded-2xl border border-edge px-5 py-3 text-center"
|
|
327
|
+
>
|
|
328
|
+
<div
|
|
329
|
+
className="text-[16px] text-content"
|
|
330
|
+
style={{ fontWeight: value, fontFamily: 'var(--font-system)' }}
|
|
331
|
+
>
|
|
332
|
+
Aa
|
|
333
|
+
</div>
|
|
334
|
+
<div className="mt-1 text-[10px] uppercase tracking-wider text-content-muted">{label}</div>
|
|
335
|
+
<div className="font-mono text-[10px] text-accent">{value}</div>
|
|
336
|
+
</div>
|
|
337
|
+
))}
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<SubLabel>Monospace</SubLabel>
|
|
341
|
+
<div className="rounded-2xl border border-edge bg-surface px-5 py-4">
|
|
342
|
+
<div className="font-mono text-[13px] leading-relaxed text-content">
|
|
343
|
+
<span className="text-accent">const</span>{' '}graph{' '}={' '}
|
|
344
|
+
<span className="text-accent">await</span>{' '}readGraph(projectId);
|
|
345
|
+
</div>
|
|
346
|
+
<div className="mt-2 font-mono text-[10px] text-content-muted uppercase tracking-wide">
|
|
347
|
+
ui-monospace · SF Mono · Cascadia Code · Fira Code
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
</Section>
|
|
351
|
+
|
|
352
|
+
{/* ── Spacing ── */}
|
|
353
|
+
<Section title="Spacing">
|
|
354
|
+
<p className="mb-5 text-[13px] text-content-muted">
|
|
355
|
+
4px base unit. Tailwind utilities: p-2 = 8px, m-4 = 16px, gap-6 = 24px.
|
|
356
|
+
</p>
|
|
357
|
+
|
|
358
|
+
<div className="flex flex-wrap gap-4">
|
|
359
|
+
{SPACING_SCALE.map(({ name, px }) => (
|
|
360
|
+
<div
|
|
361
|
+
key={name}
|
|
362
|
+
className="flex flex-col items-center gap-2 rounded-2xl border border-edge px-4 py-3"
|
|
363
|
+
style={{ minWidth: '64px' }}
|
|
364
|
+
>
|
|
365
|
+
<div
|
|
366
|
+
className="rounded-lg bg-accent/25"
|
|
367
|
+
style={{ width: `${Math.max(px, 8)}px`, height: `${Math.max(px, 8)}px` }}
|
|
368
|
+
/>
|
|
369
|
+
<div className="font-mono text-[11px] font-semibold text-accent">{name}</div>
|
|
370
|
+
<div className="font-mono text-[10px] text-content-muted">{px}px</div>
|
|
371
|
+
</div>
|
|
372
|
+
))}
|
|
373
|
+
</div>
|
|
374
|
+
</Section>
|
|
375
|
+
|
|
376
|
+
{/* Footer */}
|
|
377
|
+
<div className="mt-6 pb-12 text-center text-[11px] uppercase tracking-widest text-content-muted">
|
|
378
|
+
Toggle theme to preview token adaptation
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { X } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
export interface EditorTab {
|
|
5
|
+
path: string;
|
|
6
|
+
name: string;
|
|
7
|
+
language: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface EditorTabBarProps {
|
|
11
|
+
tabs: EditorTab[];
|
|
12
|
+
activeTabPath: string | null;
|
|
13
|
+
dirtyPaths: Set<string>;
|
|
14
|
+
onSelectTab: (path: string) => void;
|
|
15
|
+
onCloseTab: (path: string) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const EditorTabBar = ({ tabs, activeTabPath, dirtyPaths, onSelectTab, onCloseTab }: EditorTabBarProps) => {
|
|
19
|
+
const handleMiddleClick = useCallback((e: React.MouseEvent, path: string) => {
|
|
20
|
+
if (e.button === 1) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
onCloseTab(path);
|
|
23
|
+
}
|
|
24
|
+
}, [onCloseTab]);
|
|
25
|
+
|
|
26
|
+
const handleCloseClick = useCallback((e: React.MouseEvent, path: string) => {
|
|
27
|
+
e.stopPropagation();
|
|
28
|
+
onCloseTab(path);
|
|
29
|
+
}, [onCloseTab]);
|
|
30
|
+
|
|
31
|
+
if (tabs.length === 0) return null;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="editor-tab-bar">
|
|
35
|
+
{tabs.map((tab) => (
|
|
36
|
+
<button
|
|
37
|
+
key={tab.path}
|
|
38
|
+
className={`editor-tab ${tab.path === activeTabPath ? 'editor-tab--active' : ''}`}
|
|
39
|
+
onClick={() => onSelectTab(tab.path)}
|
|
40
|
+
onMouseDown={(e) => handleMiddleClick(e, tab.path)}
|
|
41
|
+
title={tab.path}
|
|
42
|
+
>
|
|
43
|
+
{dirtyPaths.has(tab.path) && <span className="editor-tab__dirty">●</span>}
|
|
44
|
+
<span className="editor-tab__name">{tab.name}</span>
|
|
45
|
+
<span
|
|
46
|
+
className="editor-tab__close"
|
|
47
|
+
onClick={(e) => handleCloseClick(e, tab.path)}
|
|
48
|
+
role="button"
|
|
49
|
+
tabIndex={-1}
|
|
50
|
+
>
|
|
51
|
+
<X size={12} strokeWidth={2} />
|
|
52
|
+
</span>
|
|
53
|
+
</button>
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|