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,252 +0,0 @@
1
- /**
2
- * Parser for beads data via HTTP API
3
- *
4
- * Fetches and provides typed access to beads with helper functions for
5
- * common operations.
6
- */
7
-
8
- import * as api from './api';
9
- import type { Bead, BeadStatus, Epic } from "@/types";
10
-
11
- /**
12
- * Loads beads from a project directory via API
13
- *
14
- * @param projectPath - The root path of the project
15
- * @returns Promise resolving to array of Bead objects
16
- *
17
- * @example
18
- * ```typescript
19
- * const beads = await loadProjectBeads('/path/to/project');
20
- * ```
21
- */
22
- export async function loadProjectBeads(projectPath: string): Promise<Bead[]> {
23
- try {
24
- const result = await api.beads.read(projectPath);
25
- // Ensure every bead has a comments array (defensive against null/undefined)
26
- return result.beads.map((bead) => ({
27
- ...bead,
28
- comments: bead.comments ?? [],
29
- }));
30
- } catch (error) {
31
- console.error(`Failed to load beads from ${projectPath}:`, error);
32
- return [];
33
- }
34
- }
35
-
36
- /**
37
- * Alias for loadProjectBeads for backward compatibility
38
- */
39
- export async function parseBeadsFromPath(projectPath: string): Promise<Bead[]> {
40
- return loadProjectBeads(projectPath);
41
- }
42
-
43
- /**
44
- * Groups beads by their status into a record
45
- *
46
- * @param beads - Array of Bead objects to group
47
- * @returns Record with status keys and arrays of beads as values
48
- *
49
- * @example
50
- * ```typescript
51
- * const grouped = groupBeadsByStatus(beads);
52
- * console.log(grouped.open.length); // Number of open beads
53
- * console.log(grouped.closed.length); // Number of closed beads
54
- * ```
55
- */
56
- export function groupBeadsByStatus(beads: Bead[]): Record<BeadStatus, Bead[]> {
57
- const grouped: Record<BeadStatus, Bead[]> = {
58
- open: [],
59
- in_progress: [],
60
- inreview: [],
61
- closed: [],
62
- };
63
-
64
- for (const bead of beads) {
65
- grouped[bead.status].push(bead);
66
- }
67
-
68
- // Sort each group by updated_at descending (most recent first)
69
- for (const status of Object.keys(grouped) as BeadStatus[]) {
70
- grouped[status].sort((a, b) => {
71
- const dateA = new Date(a.updated_at).getTime();
72
- const dateB = new Date(b.updated_at).getTime();
73
- return dateB - dateA;
74
- });
75
- }
76
-
77
- return grouped;
78
- }
79
-
80
- /**
81
- * Finds a bead by its ID
82
- *
83
- * @param beads - Array of Bead objects to search
84
- * @param id - The bead ID to find
85
- * @returns The matching Bead or undefined if not found
86
- *
87
- * @example
88
- * ```typescript
89
- * const bead = getBeadById(beads, 'beads-kanban-ui-323');
90
- * if (bead) {
91
- * console.log(bead.title);
92
- * }
93
- * ```
94
- */
95
- export function getBeadById(beads: Bead[], id: string): Bead | undefined {
96
- return beads.find((bead) => bead.id === id);
97
- }
98
-
99
- /**
100
- * Constructs the path to issues.jsonl from a project path
101
- *
102
- * @param projectPath - The root path of the project
103
- * @returns Path to the issues.jsonl file
104
- */
105
- export function getBeadsFilePath(projectPath: string): string {
106
- // Normalize path separators and ensure no trailing slash
107
- const normalizedPath = projectPath.replace(/\\/g, "/").replace(/\/$/, "");
108
- return `${normalizedPath}/.beads/issues.jsonl`;
109
- }
110
-
111
- /**
112
- * Assigns sequential ticket numbers to beads based on creation order
113
- *
114
- * @param beads - Array of Bead objects to assign numbers to
115
- * @returns Map of bead ID to ticket number (1-indexed, oldest bead = #1)
116
- *
117
- * @example
118
- * ```typescript
119
- * const ticketNumbers = assignTicketNumbers(beads);
120
- * const ticketNum = ticketNumbers.get('beads-kanban-ui-323'); // e.g., 5
121
- * console.log(`#${ticketNum}`); // "#5"
122
- * ```
123
- */
124
- export function assignTicketNumbers(beads: Bead[]): Map<string, number> {
125
- // Sort all beads by created_at ascending (oldest first)
126
- const sortedBeads = [...beads].sort((a, b) => {
127
- const dateA = new Date(a.created_at).getTime();
128
- const dateB = new Date(b.created_at).getTime();
129
- return dateA - dateB;
130
- });
131
-
132
- // Assign 1-indexed ticket numbers
133
- const ticketNumbers = new Map<string, number>();
134
- sortedBeads.forEach((bead, index) => {
135
- ticketNumbers.set(bead.id, index + 1);
136
- });
137
-
138
- return ticketNumbers;
139
- }
140
-
141
- /**
142
- * Groups beads by epic status for epic-specific views
143
- *
144
- * @param beads - Array of Bead objects to group
145
- * @returns Record with epic status keys (with_children, standalone) and arrays of beads
146
- *
147
- * @example
148
- * ```typescript
149
- * const grouped = groupByEpicStatus(beads);
150
- * console.log(grouped.epics.length); // Number of epic beads
151
- * console.log(grouped.standalone.length); // Number of standalone task beads
152
- * ```
153
- */
154
- export function groupByEpicStatus(beads: Bead[]): {
155
- epics: Epic[];
156
- standalone: Bead[];
157
- children: Bead[];
158
- } {
159
- const epics: Epic[] = [];
160
- const standalone: Bead[] = [];
161
- const children: Bead[] = [];
162
-
163
- for (const bead of beads) {
164
- // Epic: has issue_type 'epic' or has children
165
- if (bead.issue_type === 'epic' || (bead.children && bead.children.length > 0)) {
166
- epics.push({
167
- ...bead,
168
- issue_type: 'epic',
169
- children: bead.children ?? [],
170
- } as Epic);
171
- }
172
- // Child: has parent_id
173
- else if (bead.parent_id) {
174
- children.push(bead);
175
- }
176
- // Standalone: no parent, not an epic
177
- else {
178
- standalone.push(bead);
179
- }
180
- }
181
-
182
- return { epics, standalone, children };
183
- }
184
-
185
- /**
186
- * Gets all child beads for a specific epic
187
- *
188
- * @param epicId - The ID of the epic to get children for
189
- * @param beads - Array of all beads to search
190
- * @returns Array of child beads belonging to the epic
191
- *
192
- * @example
193
- * ```typescript
194
- * const children = getEpicChildren('epic-123', allBeads);
195
- * console.log(`Epic has ${children.length} children`);
196
- * ```
197
- */
198
- export function getEpicChildren(epicId: string, beads: Bead[]): Bead[] {
199
- if (!epicId || !beads || beads.length === 0) {
200
- return [];
201
- }
202
-
203
- // Find the epic first
204
- const epic = beads.find((b) => b.id === epicId);
205
- if (!epic || !epic.children || epic.children.length === 0) {
206
- return [];
207
- }
208
-
209
- // Create a lookup map for fast access
210
- const beadMap = new Map<string, Bead>();
211
- for (const bead of beads) {
212
- beadMap.set(bead.id, bead);
213
- }
214
-
215
- // Resolve children
216
- return epic.children
217
- .map((childId) => beadMap.get(childId))
218
- .filter((child): child is Bead => child !== undefined);
219
- }
220
-
221
- /**
222
- * Checks if an epic is completed (all children closed)
223
- *
224
- * @param epic - The epic bead to check
225
- * @param beads - Array of all beads to resolve children from
226
- * @returns True if all children are closed, false otherwise
227
- *
228
- * @example
229
- * ```typescript
230
- * if (isEpicCompleted(epic, allBeads)) {
231
- * console.log('Epic is fully completed!');
232
- * }
233
- * ```
234
- */
235
- export function isEpicCompleted(epic: Epic, beads: Bead[]): boolean {
236
- if (!epic.children || epic.children.length === 0) {
237
- // Epic with no children is considered completed
238
- return true;
239
- }
240
-
241
- if (!beads || beads.length === 0) {
242
- return false;
243
- }
244
-
245
- const children = getEpicChildren(epic.id, beads);
246
- if (children.length === 0) {
247
- return false;
248
- }
249
-
250
- // All children must be closed
251
- return children.every((child) => child.status === 'closed');
252
- }
package/src/lib/cli.ts DELETED
@@ -1,193 +0,0 @@
1
- /**
2
- * CLI wrapper for bd (beads) commands via HTTP API
3
- *
4
- * Provides typed async functions for interacting with the bd CLI tool
5
- * through the backend API.
6
- */
7
-
8
- import * as api from './api';
9
- import type { BeadStatus } from "@/types";
10
-
11
- /**
12
- * Result from executing a CLI command
13
- */
14
- export interface CommandResult {
15
- success: boolean;
16
- stdout: string;
17
- stderr: string;
18
- code: number | null;
19
- }
20
-
21
- /**
22
- * Execute a bd CLI command with the given arguments
23
- *
24
- * @param args - Array of command arguments (excluding 'bd')
25
- * @param cwd - Working directory for the command
26
- * @returns Promise resolving to command result
27
- */
28
- async function executeBdCommand(
29
- args: string[],
30
- cwd?: string
31
- ): Promise<CommandResult> {
32
- const result = await api.bd.command(args, cwd);
33
-
34
- return {
35
- success: result.code === 0,
36
- stdout: result.stdout,
37
- stderr: result.stderr,
38
- code: result.code,
39
- };
40
- }
41
-
42
- /**
43
- * Add a comment to a bead
44
- *
45
- * Executes: bd comment <beadId> "<message>"
46
- *
47
- * @param beadId - The ID of the bead to comment on
48
- * @param message - The comment message
49
- * @param cwd - Working directory (project path)
50
- * @throws Error if command fails
51
- *
52
- * @example
53
- * ```typescript
54
- * await addComment('BD-001', 'Fixed the bug', '/path/to/project');
55
- * ```
56
- */
57
- export async function addComment(
58
- beadId: string,
59
- message: string,
60
- cwd?: string
61
- ): Promise<void> {
62
- const result = await executeBdCommand(["comment", beadId, message], cwd);
63
-
64
- if (!result.success) {
65
- throw new Error(result.stderr || `Failed to add comment: exit code ${result.code}`);
66
- }
67
- }
68
-
69
- /**
70
- * Update the status of a bead
71
- *
72
- * Executes: bd update <beadId> --status <status>
73
- *
74
- * @param beadId - The ID of the bead to update
75
- * @param status - The new status value
76
- * @param cwd - Working directory (project path)
77
- * @throws Error if command fails
78
- *
79
- * @example
80
- * ```typescript
81
- * await updateStatus('BD-001', 'in_progress', '/path/to/project');
82
- * ```
83
- */
84
- export async function updateStatus(
85
- beadId: string,
86
- status: BeadStatus,
87
- cwd?: string
88
- ): Promise<void> {
89
- const result = await executeBdCommand(
90
- ["update", beadId, "--status", status],
91
- cwd
92
- );
93
-
94
- if (!result.success) {
95
- throw new Error(result.stderr || `Failed to update status: exit code ${result.code}`);
96
- }
97
- }
98
-
99
- /**
100
- * Close a bead
101
- *
102
- * Executes: bd close <beadId>
103
- *
104
- * @param beadId - The ID of the bead to close
105
- * @param cwd - Working directory (project path)
106
- * @throws Error if command fails
107
- *
108
- * @example
109
- * ```typescript
110
- * await closeBead('BD-001', '/path/to/project');
111
- * ```
112
- */
113
- export async function closeBead(beadId: string, cwd?: string): Promise<void> {
114
- const result = await executeBdCommand(["close", beadId], cwd);
115
-
116
- if (!result.success) {
117
- throw new Error(result.stderr || `Failed to close bead: exit code ${result.code}`);
118
- }
119
- }
120
-
121
- /**
122
- * Create a new bead
123
- *
124
- * Executes: bd create "<title>" -d "<description>"
125
- *
126
- * @param title - The bead title
127
- * @param description - The bead description
128
- * @param cwd - Working directory (project path)
129
- * @returns The created bead ID (if parseable from output)
130
- * @throws Error if command fails
131
- *
132
- * @example
133
- * ```typescript
134
- * const id = await createBead('Fix bug', 'Bug in login form', '/path/to/project');
135
- * ```
136
- */
137
- export async function createBead(
138
- title: string,
139
- description: string,
140
- cwd?: string
141
- ): Promise<string | null> {
142
- const result = await executeBdCommand(
143
- ["create", title, "-d", description],
144
- cwd
145
- );
146
-
147
- if (!result.success) {
148
- throw new Error(result.stderr || `Failed to create bead: exit code ${result.code}`);
149
- }
150
-
151
- // Try to extract bead ID from output (format varies by CLI version)
152
- const idMatch = result.stdout.match(/(?:BD-|bd-)[\w-]+/i);
153
- return idMatch ? idMatch[0] : null;
154
- }
155
-
156
- /**
157
- * Get bead details
158
- *
159
- * Executes: bd show <beadId>
160
- *
161
- * @param beadId - The ID of the bead to show
162
- * @param cwd - Working directory (project path)
163
- * @returns Raw output from bd show command
164
- * @throws Error if command fails
165
- */
166
- export async function showBead(beadId: string, cwd?: string): Promise<string> {
167
- const result = await executeBdCommand(["show", beadId], cwd);
168
-
169
- if (!result.success) {
170
- throw new Error(result.stderr || `Failed to show bead: exit code ${result.code}`);
171
- }
172
-
173
- return result.stdout;
174
- }
175
-
176
- /**
177
- * List all beads
178
- *
179
- * Executes: bd list
180
- *
181
- * @param cwd - Working directory (project path)
182
- * @returns Raw output from bd list command
183
- * @throws Error if command fails
184
- */
185
- export async function listBeads(cwd?: string): Promise<string> {
186
- const result = await executeBdCommand(["list"], cwd);
187
-
188
- if (!result.success) {
189
- throw new Error(result.stderr || `Failed to list beads: exit code ${result.code}`);
190
- }
191
-
192
- return result.stdout;
193
- }
package/src/lib/db.ts DELETED
@@ -1,145 +0,0 @@
1
- /**
2
- * Database client for HTTP API operations
3
- *
4
- * Provides typed wrappers around API calls for projects, tags,
5
- * and their relationships.
6
- */
7
-
8
- import * as api from './api';
9
- import type { Project, Tag } from '@/types';
10
-
11
- // ===== Input Types =====
12
-
13
- export type { Project, Tag };
14
-
15
- export interface CreateProjectInput {
16
- name: string;
17
- path: string;
18
- }
19
-
20
- export interface UpdateProjectInput {
21
- id: string;
22
- name?: string;
23
- path?: string;
24
- }
25
-
26
- export interface CreateTagInput {
27
- name: string;
28
- color: string;
29
- }
30
-
31
- export interface UpdateTagInput {
32
- id: string;
33
- name?: string;
34
- color?: string;
35
- }
36
-
37
- // ===== Project Operations =====
38
-
39
- /**
40
- * Gets all projects, ordered by last opened
41
- */
42
- export async function getProjects(): Promise<Project[]> {
43
- return api.projects.list();
44
- }
45
-
46
- /**
47
- * Creates a new project
48
- */
49
- export async function createProject(input: CreateProjectInput): Promise<Project> {
50
- return api.projects.create(input);
51
- }
52
-
53
- /**
54
- * Updates an existing project
55
- */
56
- export async function updateProject(input: UpdateProjectInput): Promise<Project> {
57
- const { id, ...data } = input;
58
- return api.projects.update(id, data);
59
- }
60
-
61
- /**
62
- * Deletes a project by ID
63
- */
64
- export async function deleteProject(id: string): Promise<void> {
65
- return api.projects.delete(id);
66
- }
67
-
68
- // ===== Tag Operations =====
69
-
70
- /**
71
- * Gets all tags
72
- */
73
- export async function getTags(): Promise<Tag[]> {
74
- return api.tags.list();
75
- }
76
-
77
- /**
78
- * Creates a new tag
79
- */
80
- export async function createTag(input: CreateTagInput): Promise<Tag> {
81
- return api.tags.create(input);
82
- }
83
-
84
- /**
85
- * Updates an existing tag
86
- */
87
- export async function updateTag(input: UpdateTagInput): Promise<Tag> {
88
- const { id, ...data } = input;
89
- // Note: API doesn't have a separate update endpoint, using create for now
90
- // This would need backend support for tag updates
91
- return api.tags.create(data as CreateTagInput);
92
- }
93
-
94
- /**
95
- * Deletes a tag by ID
96
- */
97
- export async function deleteTag(id: string): Promise<void> {
98
- return api.tags.delete(id);
99
- }
100
-
101
- // ===== Project-Tag Relationships =====
102
-
103
- /**
104
- * Gets all tags for a specific project
105
- */
106
- export async function getProjectTags(projectId: string): Promise<Tag[]> {
107
- const projects = await api.projects.list();
108
- const project = projects.find((p) => p.id === projectId);
109
- return project?.tags || [];
110
- }
111
-
112
- /**
113
- * Adds a tag to a project
114
- */
115
- export async function addTagToProject(projectId: string, tagId: string): Promise<void> {
116
- return api.tags.addToProject(projectId, tagId);
117
- }
118
-
119
- /**
120
- * Removes a tag from a project
121
- */
122
- export async function removeTagFromProject(projectId: string, tagId: string): Promise<void> {
123
- return api.tags.removeFromProject(projectId, tagId);
124
- }
125
-
126
- // ===== Convenience Functions =====
127
-
128
- /**
129
- * Gets a project with its tags
130
- */
131
- export async function getProjectWithTags(projectId: string): Promise<Project> {
132
- const projects = await getProjects();
133
- const project = projects.find((p) => p.id === projectId);
134
- if (!project) {
135
- throw new Error(`Project not found: ${projectId}`);
136
- }
137
- return project;
138
- }
139
-
140
- /**
141
- * Gets all projects with their tags
142
- */
143
- export async function getProjectsWithTags(): Promise<Project[]> {
144
- return getProjects();
145
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * Design Doc Utilities
3
- * Shared functions for fetching and processing design documents
4
- */
5
-
6
- const API_BASE = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:3008';
7
-
8
- /**
9
- * Fetch design doc content from the backend API
10
- */
11
- export async function fetchDesignDoc(path: string, projectPath: string): Promise<string> {
12
- const encodedPath = encodeURIComponent(path);
13
- const encodedProjectPath = encodeURIComponent(projectPath);
14
- const response = await fetch(
15
- `${API_BASE}/api/fs/read?path=${encodedPath}&project_path=${encodedProjectPath}`
16
- );
17
- if (!response.ok) {
18
- throw new Error('Failed to fetch design doc: ' + response.statusText);
19
- }
20
- const data = await response.json();
21
- return data.content || '';
22
- }
23
-
24
- /**
25
- * Strip markdown syntax and convert to plain text preview
26
- * Removes headers, links, code blocks, bold, italic, etc.
27
- */
28
- export function truncateMarkdownToPlainText(markdown: string, maxChars: number = 180): string {
29
- let text = markdown;
30
-
31
- // Remove code blocks
32
- text = text.replace(/```[\s\S]*?```/g, '');
33
-
34
- // Remove inline code
35
- text = text.replace(/`[^`]+`/g, '');
36
-
37
- // Remove headers (# ## ###)
38
- text = text.replace(/^#{1,6}\s+/gm, '');
39
-
40
- // Remove links but keep text: [text](url) -> text
41
- text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
42
-
43
- // Remove bold/italic: **text** or *text* -> text
44
- text = text.replace(/\*\*([^*]+)\*\*/g, '$1');
45
- text = text.replace(/\*([^*]+)\*/g, '$1');
46
-
47
- // Remove blockquotes
48
- text = text.replace(/^>\s+/gm, '');
49
-
50
- // Remove horizontal rules
51
- text = text.replace(/^-{3,}$/gm, '');
52
-
53
- // Remove list markers
54
- text = text.replace(/^[\s]*[-*+]\s+/gm, '');
55
- text = text.replace(/^[\s]*\d+\.\s+/gm, '');
56
-
57
- // Collapse multiple newlines
58
- text = text.replace(/\n{2,}/g, ' ');
59
-
60
- // Trim and truncate
61
- text = text.trim();
62
-
63
- if (text.length <= maxChars) {
64
- return text;
65
- }
66
-
67
- return text.slice(0, maxChars).trim() + '…';
68
- }
69
-
70
- /**
71
- * Shared prose classes for markdown rendering
72
- * Ensures consistent styling across preview and full view
73
- */
74
- export const designDocProseClasses = "prose prose-sm dark:prose-invert max-w-none prose-headings:scroll-mt-20 prose-pre:bg-zinc-900 prose-pre:text-zinc-100 prose-code:text-sm prose-code:bg-zinc-100 dark:prose-code:bg-zinc-800 prose-code:px-1 prose-code:py-0.5 prose-code:rounded";