beads-kanban-ui 0.1.0 → 0.1.1

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 (154) hide show
  1. package/README.md +16 -222
  2. package/package.json +18 -55
  3. package/.designs/beads-kanban-ui-bj0.md +0 -73
  4. package/.designs/beads-kanban-ui-qxq.md +0 -144
  5. package/.designs/epic-support.md +0 -282
  6. package/.env.local.example +0 -2
  7. package/.eslintrc.json +0 -3
  8. package/.gitattributes +0 -3
  9. package/.github/workflows/release.yml +0 -123
  10. package/.history/README_20260121193710.md +0 -227
  11. package/.history/README_20260121193918.md +0 -227
  12. package/.history/README_20260121193921.md +0 -227
  13. package/.history/README_20260121193933.md +0 -227
  14. package/.history/README_20260121193934.md +0 -227
  15. package/.history/README_20260121193944.md +0 -227
  16. package/.history/README_20260121193953.md +0 -227
  17. package/.history/src/app/page_20260121133429.tsx +0 -134
  18. package/.history/src/app/page_20260121133928.tsx +0 -134
  19. package/.history/src/app/page_20260121144850.tsx +0 -138
  20. package/.history/src/app/page_20260121144854.tsx +0 -138
  21. package/.history/src/app/page_20260121144858.tsx +0 -138
  22. package/.history/src/app/page_20260121144902.tsx +0 -138
  23. package/.history/src/app/page_20260121144906.tsx +0 -138
  24. package/.history/src/app/page_20260121144911.tsx +0 -138
  25. package/.history/src/app/page_20260121144928.tsx +0 -138
  26. package/.playwright-mcp/.playwright-mcp/morphing-dialog-wheel-scroll-fix.png +0 -0
  27. package/.playwright-mcp/beams-test.png +0 -0
  28. package/.playwright-mcp/card-verification.png +0 -0
  29. package/.playwright-mcp/design-doc-dialog-fix-verification.png +0 -0
  30. package/.playwright-mcp/dialog-width-test.png +0 -0
  31. package/.playwright-mcp/homepage.png +0 -0
  32. package/.playwright-mcp/morphing-dialog-expanded.png +0 -0
  33. package/.playwright-mcp/morphing-dialog-fixes-final.png +0 -0
  34. package/.playwright-mcp/morphing-dialog-open.png +0 -0
  35. package/.playwright-mcp/page-2026-01-21T14-08-31-529Z.png +0 -0
  36. package/.playwright-mcp/page-2026-01-21T14-09-23-431Z.png +0 -0
  37. package/.playwright-mcp/page-2026-01-21T14-10-28-773Z.png +0 -0
  38. package/.playwright-mcp/page-2026-01-21T14-10-47-432Z.png +0 -0
  39. package/.playwright-mcp/page-2026-01-21T14-11-12-350Z.png +0 -0
  40. package/.playwright-mcp/screenshot-after-click.png +0 -0
  41. package/.playwright-mcp/screenshot-after-dialog-click.png +0 -0
  42. package/.playwright-mcp/sheet-restored-after-dialog-close.png +0 -0
  43. package/.playwright-mcp/test-1-sheet-open-with-overlay.png +0 -0
  44. package/.playwright-mcp/test-2-morphing-dialog-with-overlay.png +0 -0
  45. package/.playwright-mcp/test-3-sheet-open-dark-overlay.png +0 -0
  46. package/.playwright-mcp/test-4-morphing-dialog-with-dark-overlay.png +0 -0
  47. package/.playwright-mcp/test-5-morphing-dialog-scrolled.png +0 -0
  48. package/.playwright-mcp/test-6-sheet-restored-after-dialog-close.png +0 -0
  49. package/.playwright-mcp/wheel-scroll-fixed.png +0 -0
  50. package/Screenshots/bead-detail.png +0 -0
  51. package/Screenshots/dashboard.png +0 -0
  52. package/Screenshots/kanban-board.png +0 -0
  53. package/components.json +0 -27
  54. package/logo/logo.svg +0 -1
  55. package/next.config.js +0 -9
  56. package/npm/README.md +0 -37
  57. package/npm/package.json +0 -20
  58. package/postcss.config.js +0 -6
  59. package/public/logo.svg +0 -1
  60. package/restart.sh +0 -5
  61. package/server/Cargo.lock +0 -1685
  62. package/server/Cargo.toml +0 -24
  63. package/server/src/db.rs +0 -570
  64. package/server/src/main.rs +0 -141
  65. package/server/src/routes/beads.rs +0 -413
  66. package/server/src/routes/cli.rs +0 -150
  67. package/server/src/routes/fs.rs +0 -360
  68. package/server/src/routes/git.rs +0 -169
  69. package/server/src/routes/mod.rs +0 -107
  70. package/server/src/routes/projects.rs +0 -177
  71. package/server/src/routes/watch.rs +0 -211
  72. package/src/app/globals.css +0 -101
  73. package/src/app/layout.tsx +0 -36
  74. package/src/app/page.tsx +0 -348
  75. package/src/app/project/kanban-board.tsx +0 -356
  76. package/src/app/project/page.tsx +0 -18
  77. package/src/app/settings/page.tsx +0 -224
  78. package/src/components/Beams.css +0 -5
  79. package/src/components/Beams.jsx +0 -307
  80. package/src/components/Galaxy.css +0 -5
  81. package/src/components/Galaxy.jsx +0 -333
  82. package/src/components/activity-timeline.tsx +0 -172
  83. package/src/components/add-project-dialog.tsx +0 -219
  84. package/src/components/bead-card.tsx +0 -196
  85. package/src/components/bead-detail.tsx +0 -306
  86. package/src/components/color-picker.tsx +0 -101
  87. package/src/components/comment-input.tsx +0 -155
  88. package/src/components/comment-list.tsx +0 -147
  89. package/src/components/dependency-badge.tsx +0 -106
  90. package/src/components/design-doc-dialog.tsx +0 -58
  91. package/src/components/design-doc-preview.tsx +0 -97
  92. package/src/components/design-doc-viewer.tsx +0 -199
  93. package/src/components/editable-project-name.tsx +0 -178
  94. package/src/components/epic-card.tsx +0 -263
  95. package/src/components/folder-browser.tsx +0 -273
  96. package/src/components/footer.tsx +0 -27
  97. package/src/components/kanban/default.tsx +0 -184
  98. package/src/components/kanban-column.tsx +0 -167
  99. package/src/components/project-card.tsx +0 -191
  100. package/src/components/quick-filter-bar.tsx +0 -279
  101. package/src/components/scan-directory-dialog.tsx +0 -368
  102. package/src/components/status-donut.tsx +0 -197
  103. package/src/components/subtask-list.tsx +0 -128
  104. package/src/components/tag-picker.tsx +0 -252
  105. package/src/components/ui/.gitkeep +0 -0
  106. package/src/components/ui/alert-dialog.tsx +0 -141
  107. package/src/components/ui/avatar.tsx +0 -67
  108. package/src/components/ui/badge.tsx +0 -230
  109. package/src/components/ui/button.tsx +0 -433
  110. package/src/components/ui/card/index.tsx +0 -24
  111. package/src/components/ui/card/roiui-card.module.css +0 -197
  112. package/src/components/ui/card/roiui-card.tsx +0 -154
  113. package/src/components/ui/card/shadcn-card.tsx +0 -76
  114. package/src/components/ui/chart.tsx +0 -369
  115. package/src/components/ui/dialog.tsx +0 -122
  116. package/src/components/ui/dropdown-menu.tsx +0 -201
  117. package/src/components/ui/input.tsx +0 -22
  118. package/src/components/ui/kanban.tsx +0 -522
  119. package/src/components/ui/morphing-dialog.tsx +0 -457
  120. package/src/components/ui/popover.tsx +0 -33
  121. package/src/components/ui/progress.tsx +0 -28
  122. package/src/components/ui/scroll-area.tsx +0 -48
  123. package/src/components/ui/select.tsx +0 -159
  124. package/src/components/ui/separator.tsx +0 -31
  125. package/src/components/ui/sheet.tsx +0 -142
  126. package/src/components/ui/skeleton.tsx +0 -15
  127. package/src/components/ui/toast.tsx +0 -129
  128. package/src/components/ui/toaster.tsx +0 -35
  129. package/src/components/ui/tooltip.tsx +0 -30
  130. package/src/hooks/.gitkeep +0 -0
  131. package/src/hooks/use-bead-filters.ts +0 -261
  132. package/src/hooks/use-beads.ts +0 -162
  133. package/src/hooks/use-branch-statuses.ts +0 -161
  134. package/src/hooks/use-epics.ts +0 -173
  135. package/src/hooks/use-file-watcher.ts +0 -111
  136. package/src/hooks/use-keyboard-navigation.ts +0 -282
  137. package/src/hooks/use-project.ts +0 -61
  138. package/src/hooks/use-projects.ts +0 -93
  139. package/src/hooks/use-toast.ts +0 -194
  140. package/src/hooks/useClickOutside.tsx +0 -26
  141. package/src/lib/.gitkeep +0 -0
  142. package/src/lib/api.ts +0 -186
  143. package/src/lib/beads-parser.ts +0 -252
  144. package/src/lib/cli.ts +0 -193
  145. package/src/lib/db.ts +0 -145
  146. package/src/lib/design-doc.ts +0 -74
  147. package/src/lib/epic-parser.ts +0 -242
  148. package/src/lib/git.ts +0 -102
  149. package/src/lib/utils.ts +0 -12
  150. package/src/types/index.ts +0 -107
  151. package/tailwind.config.ts +0 -85
  152. package/tsconfig.json +0 -26
  153. /package/{npm/bin → bin}/cli.js +0 -0
  154. /package/{npm/scripts → scripts}/postinstall.js +0 -0
@@ -1,178 +0,0 @@
1
- "use client";
2
-
3
- import { useState, useRef, useEffect } from "react";
4
- import { Pencil, Check, X, Loader2 } from "lucide-react";
5
- import { Button } from "@/components/ui/button";
6
- import { Input } from "@/components/ui/input";
7
- import {
8
- Popover,
9
- PopoverContent,
10
- PopoverTrigger,
11
- } from "@/components/ui/popover";
12
- import { useToast } from "@/hooks/use-toast";
13
- import { updateProject } from "@/lib/db";
14
-
15
- /**
16
- * Converts kebab-case, snake_case, camelCase to Title Case with spaces
17
- */
18
- function formatProjectName(name: string): string {
19
- return name
20
- .replace(/[-_]/g, ' ') // Replace hyphens and underscores with spaces
21
- .replace(/([a-z])([A-Z])/g, '$1 $2') // Add space before capitals in camelCase
22
- .replace(/\b\w/g, c => c.toUpperCase()); // Capitalize first letter of each word
23
- }
24
-
25
- interface EditableProjectNameProps {
26
- projectId: string;
27
- initialName: string;
28
- onNameUpdated: () => void;
29
- }
30
-
31
- /**
32
- * Editable project name component with popover for inline editing
33
- */
34
- export function EditableProjectName({
35
- projectId,
36
- initialName,
37
- onNameUpdated,
38
- }: EditableProjectNameProps) {
39
- const [isOpen, setIsOpen] = useState(false);
40
- const [name, setName] = useState(initialName);
41
- const [isSubmitting, setIsSubmitting] = useState(false);
42
- const inputRef = useRef<HTMLInputElement>(null);
43
- const { toast } = useToast();
44
-
45
- // Reset name when popover opens
46
- useEffect(() => {
47
- if (isOpen) {
48
- setName(initialName);
49
- // Focus input after popover opens
50
- setTimeout(() => inputRef.current?.select(), 0);
51
- }
52
- }, [isOpen, initialName]);
53
-
54
- const handleSubmit = async (e?: React.FormEvent) => {
55
- e?.preventDefault();
56
-
57
- const trimmedName = name.trim();
58
-
59
- if (!trimmedName) {
60
- toast({
61
- title: "Invalid name",
62
- description: "Project name cannot be empty.",
63
- variant: "destructive",
64
- });
65
- return;
66
- }
67
-
68
- if (trimmedName === initialName) {
69
- setIsOpen(false);
70
- return;
71
- }
72
-
73
- setIsSubmitting(true);
74
-
75
- try {
76
- await updateProject({ id: projectId, name: trimmedName });
77
-
78
- toast({
79
- title: "Project renamed",
80
- description: `Project has been renamed to "${trimmedName}".`,
81
- });
82
-
83
- setIsOpen(false);
84
- onNameUpdated();
85
- } catch (err) {
86
- console.error("Error updating project name:", err);
87
- toast({
88
- title: "Error",
89
- description: "Failed to update project name. Please try again.",
90
- variant: "destructive",
91
- });
92
- } finally {
93
- setIsSubmitting(false);
94
- }
95
- };
96
-
97
- const handleCancel = () => {
98
- setName(initialName);
99
- setIsOpen(false);
100
- };
101
-
102
- const handleKeyDown = (e: React.KeyboardEvent) => {
103
- if (e.key === "Escape") {
104
- handleCancel();
105
- }
106
- };
107
-
108
- return (
109
- <div className="flex items-center gap-2">
110
- <h1 className="text-lg font-semibold text-zinc-100">{formatProjectName(initialName)}</h1>
111
- <Popover open={isOpen} onOpenChange={setIsOpen}>
112
- <PopoverTrigger asChild>
113
- <Button
114
- variant="ghost"
115
- size="icon"
116
- className="h-7 w-7 text-muted-foreground hover:text-foreground"
117
- aria-label="Edit project name"
118
- >
119
- <Pencil className="h-3.5 w-3.5" />
120
- </Button>
121
- </PopoverTrigger>
122
- <PopoverContent className="w-80" align="start">
123
- <form onSubmit={handleSubmit}>
124
- <div className="space-y-4">
125
- <div className="space-y-2">
126
- <label
127
- htmlFor="project-name"
128
- className="text-sm font-medium text-foreground"
129
- >
130
- Project Name
131
- </label>
132
- <Input
133
- ref={inputRef}
134
- id="project-name"
135
- value={name}
136
- onChange={(e) => setName(e.target.value)}
137
- onKeyDown={handleKeyDown}
138
- placeholder="Enter project name"
139
- disabled={isSubmitting}
140
- autoComplete="off"
141
- />
142
- </div>
143
- <div className="flex justify-end gap-2">
144
- <Button
145
- type="button"
146
- variant="outline"
147
- size="sm"
148
- onClick={handleCancel}
149
- disabled={isSubmitting}
150
- >
151
- <X className="mr-1 h-3.5 w-3.5" />
152
- Cancel
153
- </Button>
154
- <Button
155
- type="submit"
156
- size="sm"
157
- disabled={isSubmitting || !name.trim()}
158
- >
159
- {isSubmitting ? (
160
- <>
161
- <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
162
- Saving...
163
- </>
164
- ) : (
165
- <>
166
- <Check className="mr-1 h-3.5 w-3.5" />
167
- Save
168
- </>
169
- )}
170
- </Button>
171
- </div>
172
- </div>
173
- </form>
174
- </PopoverContent>
175
- </Popover>
176
- </div>
177
- );
178
- }
@@ -1,263 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { Badge } from "@/components/ui/badge";
5
- import { Progress } from "@/components/ui/progress";
6
- import { cn } from "@/lib/utils";
7
- import type { Bead, Epic, EpicProgress } from "@/types";
8
- import { ChevronDown, ChevronRight, Layers, MessageSquare } from "lucide-react";
9
- import { SubtaskList } from "@/components/subtask-list";
10
- import { DependencyBadge } from "@/components/dependency-badge";
11
- import { DesignDocPreview } from "@/components/design-doc-preview";
12
- import { computeEpicProgress } from "@/lib/epic-parser";
13
-
14
- export interface EpicCardProps {
15
- /** Epic bead with children */
16
- epic: Epic;
17
- /** All beads to resolve children */
18
- allBeads: Bead[];
19
- /** Ticket number for display */
20
- ticketNumber?: number;
21
- /** Whether this epic is selected */
22
- isSelected?: boolean;
23
- /** Callback when selecting this epic */
24
- onSelect: (epic: Epic) => void;
25
- /** Callback when clicking a child task */
26
- onChildClick: (child: Bead) => void;
27
- /** Callback when navigating to a dependency */
28
- onNavigateToDependency?: (beadId: string) => void;
29
- /** Project root path for fetching design docs */
30
- projectPath?: string;
31
- }
32
-
33
- /**
34
- * Format bead ID for display
35
- */
36
- function formatBeadId(id: string): string {
37
- if (id.startsWith("BD-") || id.startsWith("bd-")) {
38
- return id.length > 10 ? 'BD-' + id.slice(-6) : id.toUpperCase();
39
- }
40
- const parts = id.split("-");
41
- const shortId = parts[parts.length - 1];
42
- return 'BD-' + shortId.slice(0, 6);
43
- }
44
-
45
- /**
46
- * Truncate text to max length
47
- */
48
- function truncate(text: string, maxLength: number): string {
49
- if (text.length <= maxLength) return text;
50
- return text.slice(0, maxLength).trim() + "…";
51
- }
52
-
53
- /**
54
- * Compute epic progress from children
55
- * Uses epic-parser utility for proper dependency resolution
56
- */
57
- function computeProgress(epic: Epic, allBeads: Bead[]): EpicProgress {
58
- return computeEpicProgress(epic, allBeads);
59
- }
60
-
61
- /**
62
- * Get progress bar indicator color based on completion percentage
63
- */
64
- function getProgressIndicatorClass(percentage: number): string {
65
- if (percentage === 100) return "[&>*]:bg-green-500";
66
- if (percentage >= 75) return "[&>*]:bg-green-500";
67
- if (percentage >= 50) return "[&>*]:bg-blue-500";
68
- if (percentage >= 25) return "[&>*]:bg-amber-500";
69
- return "[&>*]:bg-purple-500";
70
- }
71
-
72
- /**
73
- * Larger epic card with distinctive styling
74
- */
75
- export function EpicCard({
76
- epic,
77
- allBeads,
78
- ticketNumber,
79
- isSelected = false,
80
- onSelect,
81
- onChildClick,
82
- onNavigateToDependency,
83
- projectPath
84
- }: EpicCardProps) {
85
- const [isExpanded, setIsExpanded] = useState(false);
86
- const [isDesignPreviewExpanded, setIsDesignPreviewExpanded] = useState(false);
87
-
88
- // Resolve children from IDs
89
- const children = (epic.children || [])
90
- .map(childId => allBeads.find(b => b.id === childId))
91
- .filter((b): b is Bead => b !== undefined);
92
-
93
- const progress = computeProgress(epic, allBeads);
94
- const progressPercentage = progress.total > 0
95
- ? Math.round((progress.completed / progress.total) * 100)
96
- : 0;
97
-
98
- const commentCount = (epic.comments ?? []).length;
99
- const hasDesignDoc = !!epic.design_doc;
100
-
101
- return (
102
- <div
103
- data-bead-id={epic.id}
104
- role="button"
105
- tabIndex={0}
106
- aria-label={`Select epic: ${epic.title}`}
107
- className={cn(
108
- "rounded-lg cursor-pointer p-4",
109
- "bg-zinc-900/70 backdrop-blur-md",
110
- "border border-zinc-800/60 border-l-4 border-l-purple-500",
111
- "shadow-sm shadow-black/20",
112
- "transition-[transform,box-shadow,border-color] duration-200",
113
- "hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/30",
114
- "hover:border-zinc-700",
115
- "focus:outline-none focus-visible:ring-2 focus-visible:ring-purple-400 focus-visible:ring-offset-2 focus-visible:ring-offset-[#0a0a0a]",
116
- isSelected && "ring-2 ring-purple-400 ring-offset-2 ring-offset-[#0a0a0a]"
117
- )}
118
- onClick={() => onSelect(epic)}
119
- onKeyDown={(e) => {
120
- if (e.key === 'Enter' || e.key === ' ') {
121
- e.preventDefault();
122
- onSelect(epic);
123
- }
124
- }}
125
- >
126
- <div className="space-y-3">
127
- {/* Header: Ticket # + Epic Icon + ID + Dependencies */}
128
- <div className="flex items-center justify-between">
129
- <div className="flex items-center gap-2">
130
- <Layers className="h-4 w-4 text-purple-400" aria-hidden="true" />
131
- <span className="text-xs font-mono text-zinc-400">
132
- {ticketNumber !== undefined && (
133
- <span className="font-semibold text-white">#{ticketNumber}</span>
134
- )}
135
- {ticketNumber !== undefined && " "}
136
- {formatBeadId(epic.id)}
137
- </span>
138
- </div>
139
- <div className="flex items-center gap-1.5">
140
- <DependencyBadge
141
- deps={epic.deps}
142
- blockers={epic.blockers}
143
- onNavigate={onNavigateToDependency}
144
- />
145
- <Badge
146
- variant="outline"
147
- className="text-[10px] px-2 py-0.5 border-purple-500/30 text-purple-400 bg-purple-500/20 font-semibold"
148
- >
149
- EPIC
150
- </Badge>
151
- </div>
152
- </div>
153
-
154
- {/* Title */}
155
- <h3 className="font-bold text-base leading-tight text-purple-100">
156
- {truncate(epic.title, 60)}
157
- </h3>
158
-
159
- {/* Description */}
160
- {epic.description && (
161
- <p className="text-xs text-zinc-400 leading-relaxed">
162
- {truncate(epic.description, 100)}
163
- </p>
164
- )}
165
-
166
- {/* Progress Bar */}
167
- <div className="space-y-1.5">
168
- <div className="flex items-center justify-between text-xs">
169
- <span className="text-zinc-400">
170
- Progress: {progress.completed}/{progress.total} completed
171
- </span>
172
- <span className="font-semibold text-zinc-300">{progressPercentage}%</span>
173
- </div>
174
- <Progress
175
- value={progressPercentage}
176
- aria-label={`Epic progress: ${progress.completed} of ${progress.total} completed`}
177
- className={cn(
178
- "h-2 bg-zinc-800",
179
- getProgressIndicatorClass(progressPercentage)
180
- )}
181
- />
182
- <div className="flex items-center gap-3 text-[10px] text-zinc-500">
183
- <span className="flex items-center gap-1">
184
- <div className="w-2 h-2 rounded-full bg-blue-500" aria-hidden="true" />
185
- {progress.inProgress} in progress
186
- </span>
187
- {progress.blocked > 0 && (
188
- <span className="flex items-center gap-1">
189
- <div className="w-2 h-2 rounded-full bg-red-500" aria-hidden="true" />
190
- {progress.blocked} blocked
191
- </span>
192
- )}
193
- </div>
194
- </div>
195
-
196
- {/* Design Doc Preview */}
197
- {hasDesignDoc && projectPath && (
198
- <div className="pt-2 border-t border-zinc-700">
199
- <button
200
- onClick={(e) => {
201
- e.stopPropagation();
202
- setIsDesignPreviewExpanded(!isDesignPreviewExpanded);
203
- }}
204
- aria-expanded={isDesignPreviewExpanded}
205
- aria-label={`${isDesignPreviewExpanded ? 'Collapse' : 'Expand'} design preview`}
206
- className="flex items-center gap-1 text-xs font-semibold text-purple-400 hover:text-purple-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-400 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-900 rounded mb-2"
207
- >
208
- {isDesignPreviewExpanded ? (
209
- <ChevronDown className="h-3.5 w-3.5" aria-hidden="true" />
210
- ) : (
211
- <ChevronRight className="h-3.5 w-3.5" aria-hidden="true" />
212
- )}
213
- Design Preview
214
- </button>
215
- {isDesignPreviewExpanded && (
216
- <DesignDocPreview
217
- designDocPath={epic.design_doc!}
218
- epicId={epic.id}
219
- projectPath={projectPath}
220
- />
221
- )}
222
- </div>
223
- )}
224
-
225
- {/* Children Preview/List */}
226
- <div className="pt-2 border-t border-zinc-700">
227
- <button
228
- onClick={(e) => {
229
- e.stopPropagation();
230
- setIsExpanded(!isExpanded);
231
- }}
232
- aria-expanded={isExpanded}
233
- aria-label={`${isExpanded ? 'Collapse' : 'Expand'} child tasks`}
234
- className="flex items-center gap-1 text-xs font-semibold text-purple-400 hover:text-purple-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-purple-400 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-900 rounded mb-2"
235
- >
236
- {isExpanded ? (
237
- <ChevronDown className="h-3.5 w-3.5" aria-hidden="true" />
238
- ) : (
239
- <ChevronRight className="h-3.5 w-3.5" aria-hidden="true" />
240
- )}
241
- Child Tasks ({children.length})
242
- </button>
243
- <SubtaskList
244
- childTasks={children}
245
- onChildClick={onChildClick}
246
- maxCollapsed={3}
247
- isExpanded={isExpanded}
248
- />
249
- </div>
250
-
251
- {/* Footer: comment count */}
252
- {commentCount > 0 && (
253
- <div className="flex items-center pt-2">
254
- <span className="flex items-center gap-1 text-[10px] text-muted-foreground">
255
- <MessageSquare className="h-3 w-3" aria-hidden="true" />
256
- {commentCount} {commentCount === 1 ? "comment" : "comments"}
257
- </span>
258
- </div>
259
- )}
260
- </div>
261
- </div>
262
- );
263
- }