beads-kanban-ui 0.1.0 → 0.1.2

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,177 +0,0 @@
1
- //! Project and Tag REST API routes
2
- //!
3
- //! Provides CRUD endpoints for projects, tags, and project-tag relationships.
4
-
5
- use axum::{
6
- extract::{Path, State},
7
- http::StatusCode,
8
- Json,
9
- };
10
- use serde::Serialize;
11
- use std::sync::Arc;
12
-
13
- use crate::db::{
14
- CreateProjectInput, CreateTagInput, Database, DbError, ProjectTagInput, ProjectWithTags, Tag,
15
- UpdateProjectInput,
16
- };
17
-
18
- /// Application state containing the database
19
- pub type AppState = Arc<Database>;
20
-
21
- /// Error response structure
22
- #[derive(Serialize)]
23
- pub struct ErrorResponse {
24
- pub error: String,
25
- }
26
-
27
- /// Success response structure for operations that don't return data
28
- #[derive(Serialize)]
29
- pub struct SuccessResponse {
30
- pub success: bool,
31
- }
32
-
33
- impl DbError {
34
- fn status_code(&self) -> StatusCode {
35
- match self {
36
- DbError::ProjectNotFound(_) | DbError::TagNotFound(_) => StatusCode::NOT_FOUND,
37
- DbError::Sqlite(_) | DbError::PathError => StatusCode::INTERNAL_SERVER_ERROR,
38
- }
39
- }
40
- }
41
-
42
- fn db_error_response(err: DbError) -> (StatusCode, Json<ErrorResponse>) {
43
- let status = err.status_code();
44
- (
45
- status,
46
- Json(ErrorResponse {
47
- error: err.to_string(),
48
- }),
49
- )
50
- }
51
-
52
- // ===== Project Routes =====
53
-
54
- /// GET /api/projects - List all projects with their tags
55
- pub async fn list_projects(
56
- State(db): State<AppState>,
57
- ) -> Result<Json<Vec<ProjectWithTags>>, (StatusCode, Json<ErrorResponse>)> {
58
- db.get_projects_with_tags()
59
- .map(Json)
60
- .map_err(db_error_response)
61
- }
62
-
63
- /// POST /api/projects - Create a new project
64
- pub async fn create_project(
65
- State(db): State<AppState>,
66
- Json(input): Json<CreateProjectInput>,
67
- ) -> Result<(StatusCode, Json<ProjectWithTags>), (StatusCode, Json<ErrorResponse>)> {
68
- let project = db.create_project(input).map_err(db_error_response)?;
69
-
70
- // Return project with empty tags array
71
- let project_with_tags = ProjectWithTags {
72
- id: project.id,
73
- name: project.name,
74
- path: project.path,
75
- tags: vec![],
76
- last_opened: project.last_opened,
77
- created_at: project.created_at,
78
- };
79
-
80
- Ok((StatusCode::CREATED, Json(project_with_tags)))
81
- }
82
-
83
- /// PATCH /api/projects/:id - Update a project
84
- pub async fn update_project(
85
- State(db): State<AppState>,
86
- Path(id): Path<String>,
87
- Json(input): Json<UpdateProjectInput>,
88
- ) -> Result<Json<ProjectWithTags>, (StatusCode, Json<ErrorResponse>)> {
89
- let project = db.update_project(&id, input).map_err(db_error_response)?;
90
- let tags = db.get_project_tags(&id).map_err(db_error_response)?;
91
-
92
- Ok(Json(ProjectWithTags {
93
- id: project.id,
94
- name: project.name,
95
- path: project.path,
96
- tags,
97
- last_opened: project.last_opened,
98
- created_at: project.created_at,
99
- }))
100
- }
101
-
102
- /// DELETE /api/projects/:id - Delete a project
103
- pub async fn delete_project(
104
- State(db): State<AppState>,
105
- Path(id): Path<String>,
106
- ) -> Result<StatusCode, (StatusCode, Json<ErrorResponse>)> {
107
- db.delete_project(&id).map_err(db_error_response)?;
108
- Ok(StatusCode::NO_CONTENT)
109
- }
110
-
111
- // ===== Tag Routes =====
112
-
113
- /// GET /api/tags - List all tags
114
- pub async fn list_tags(
115
- State(db): State<AppState>,
116
- ) -> Result<Json<Vec<Tag>>, (StatusCode, Json<ErrorResponse>)> {
117
- db.get_tags().map(Json).map_err(db_error_response)
118
- }
119
-
120
- /// POST /api/tags - Create a new tag
121
- pub async fn create_tag(
122
- State(db): State<AppState>,
123
- Json(input): Json<CreateTagInput>,
124
- ) -> Result<(StatusCode, Json<Tag>), (StatusCode, Json<ErrorResponse>)> {
125
- let tag = db.create_tag(input).map_err(db_error_response)?;
126
- Ok((StatusCode::CREATED, Json(tag)))
127
- }
128
-
129
- /// DELETE /api/tags/:id - Delete a tag
130
- pub async fn delete_tag(
131
- State(db): State<AppState>,
132
- Path(id): Path<String>,
133
- ) -> Result<StatusCode, (StatusCode, Json<ErrorResponse>)> {
134
- db.delete_tag(&id).map_err(db_error_response)?;
135
- Ok(StatusCode::NO_CONTENT)
136
- }
137
-
138
- // ===== Project-Tag Relationship Routes =====
139
-
140
- /// POST /api/project-tags - Add a tag to a project
141
- pub async fn add_project_tag(
142
- State(db): State<AppState>,
143
- Json(input): Json<ProjectTagInput>,
144
- ) -> Result<(StatusCode, Json<SuccessResponse>), (StatusCode, Json<ErrorResponse>)> {
145
- db.add_tag_to_project(&input.project_id, &input.tag_id)
146
- .map_err(db_error_response)?;
147
- Ok((StatusCode::CREATED, Json(SuccessResponse { success: true })))
148
- }
149
-
150
- /// DELETE /api/project-tags/:project_id/:tag_id - Remove a tag from a project
151
- pub async fn remove_project_tag(
152
- State(db): State<AppState>,
153
- Path((project_id, tag_id)): Path<(String, String)>,
154
- ) -> Result<Json<SuccessResponse>, (StatusCode, Json<ErrorResponse>)> {
155
- db.remove_tag_from_project(&project_id, &tag_id)
156
- .map_err(db_error_response)?;
157
- Ok(Json(SuccessResponse { success: true }))
158
- }
159
-
160
- /// Creates the project/tag router with all routes
161
- pub fn project_routes() -> axum::Router<AppState> {
162
- use axum::routing::{delete, get, patch, post};
163
-
164
- axum::Router::new()
165
- // Project routes
166
- .route("/projects", get(list_projects).post(create_project))
167
- .route(
168
- "/projects/:id",
169
- patch(update_project).delete(delete_project),
170
- )
171
- // Tag routes
172
- .route("/tags", get(list_tags).post(create_tag))
173
- .route("/tags/:id", delete(delete_tag))
174
- // Project-tag relationship routes
175
- .route("/project-tags", post(add_project_tag))
176
- .route("/project-tags/:project_id/:tag_id", delete(remove_project_tag))
177
- }
@@ -1,211 +0,0 @@
1
- //! File watcher SSE endpoint for real-time file change notifications.
2
- //!
3
- //! Provides Server-Sent Events for monitoring changes to beads issue files.
4
-
5
- use axum::{
6
- extract::Query,
7
- response::sse::{Event, Sse},
8
- };
9
- use futures::stream::Stream;
10
- use notify::{
11
- event::ModifyKind, Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher,
12
- };
13
- use serde::{Deserialize, Serialize};
14
- use std::{convert::Infallible, path::PathBuf, time::Duration};
15
- use tokio::sync::mpsc;
16
- use tokio_stream::wrappers::ReceiverStream;
17
- use tracing::{error, info, warn};
18
-
19
- /// Query parameters for the watch endpoint.
20
- #[derive(Debug, Deserialize)]
21
- pub struct WatchParams {
22
- /// The project path to watch for changes.
23
- pub path: String,
24
- }
25
-
26
- /// File change event sent to clients.
27
- #[derive(Debug, Serialize)]
28
- pub struct FileChangeEvent {
29
- /// The path of the changed file.
30
- pub path: String,
31
- /// The type of change (modified, created, removed).
32
- #[serde(rename = "type")]
33
- pub change_type: String,
34
- }
35
-
36
- /// SSE endpoint for watching beads file changes.
37
- ///
38
- /// Monitors the `.beads/issues.jsonl` file in the specified project path
39
- /// and sends SSE events when changes are detected.
40
- ///
41
- /// # Query Parameters
42
- ///
43
- /// - `path`: The project directory path to monitor
44
- ///
45
- /// # Returns
46
- ///
47
- /// A Server-Sent Events stream of file change notifications.
48
- pub async fn watch_beads(
49
- Query(params): Query<WatchParams>,
50
- ) -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
51
- let project_path = PathBuf::from(&params.path);
52
- let beads_file = project_path.join(".beads").join("issues.jsonl");
53
-
54
- info!("Starting file watcher for: {:?}", beads_file);
55
-
56
- // Create channel for events with buffer for debouncing
57
- let (tx, rx) = mpsc::channel::<Result<Event, Infallible>>(100);
58
-
59
- // Spawn the watcher task
60
- tokio::spawn(async move {
61
- if let Err(e) = run_watcher(beads_file, tx).await {
62
- error!("File watcher error: {}", e);
63
- }
64
- });
65
-
66
- let stream = ReceiverStream::new(rx);
67
- Sse::new(stream).keep_alive(
68
- axum::response::sse::KeepAlive::new()
69
- .interval(Duration::from_secs(30))
70
- .text("ping"),
71
- )
72
- }
73
-
74
- /// Runs the file watcher and sends events through the channel.
75
- async fn run_watcher(
76
- beads_file: PathBuf,
77
- tx: mpsc::Sender<Result<Event, Infallible>>,
78
- ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
79
- // Create a channel for notify events
80
- let (notify_tx, mut notify_rx) = mpsc::channel(100);
81
-
82
- // Create the watcher
83
- let mut watcher = RecommendedWatcher::new(
84
- move |res: notify::Result<notify::Event>| {
85
- if let Ok(event) = res {
86
- // Only forward relevant events
87
- let _ = notify_tx.blocking_send(event);
88
- }
89
- },
90
- Config::default().with_poll_interval(Duration::from_millis(100)),
91
- )?;
92
-
93
- // Watch the parent directory (.beads) since the file might not exist yet
94
- let watch_path = beads_file
95
- .parent()
96
- .map(|p| p.to_path_buf())
97
- .unwrap_or_else(|| beads_file.clone());
98
-
99
- // Create the .beads directory if it doesn't exist
100
- if !watch_path.exists() {
101
- warn!(
102
- "Watch path does not exist, waiting for creation: {:?}",
103
- watch_path
104
- );
105
- }
106
-
107
- // Try to watch the path, or watch parent if it doesn't exist
108
- let actual_watch_path = if watch_path.exists() {
109
- watch_path.clone()
110
- } else if let Some(parent) = watch_path.parent() {
111
- if parent.exists() {
112
- parent.to_path_buf()
113
- } else {
114
- error!("Neither watch path nor parent exists: {:?}", watch_path);
115
- return Ok(());
116
- }
117
- } else {
118
- error!("No valid path to watch: {:?}", watch_path);
119
- return Ok(());
120
- };
121
-
122
- watcher.watch(&actual_watch_path, RecursiveMode::Recursive)?;
123
- info!("File watcher active on: {:?}", actual_watch_path);
124
-
125
- // Send initial connection event
126
- let connect_event = Event::default().data(
127
- serde_json::to_string(&FileChangeEvent {
128
- path: beads_file.to_string_lossy().to_string(),
129
- change_type: "connected".to_string(),
130
- })
131
- .unwrap_or_default(),
132
- );
133
- let _ = tx.send(Ok(connect_event)).await;
134
-
135
- // Debounce state
136
- let mut last_event_time = std::time::Instant::now();
137
- let debounce_duration = Duration::from_millis(100);
138
-
139
- // Process events
140
- while let Some(event) = notify_rx.recv().await {
141
- // Check if the event is for our target file
142
- let is_relevant = event.paths.iter().any(|p| {
143
- p.ends_with("issues.jsonl")
144
- || p.ends_with(".beads")
145
- || p == &beads_file
146
- });
147
-
148
- if !is_relevant {
149
- continue;
150
- }
151
-
152
- // Debounce rapid changes
153
- let now = std::time::Instant::now();
154
- if now.duration_since(last_event_time) < debounce_duration {
155
- continue;
156
- }
157
- last_event_time = now;
158
-
159
- // Determine event type
160
- let change_type = match event.kind {
161
- EventKind::Create(_) => "created",
162
- EventKind::Modify(ModifyKind::Data(_)) => "modified",
163
- EventKind::Modify(_) => "modified",
164
- EventKind::Remove(_) => "removed",
165
- _ => continue, // Ignore other events
166
- };
167
-
168
- let file_event = FileChangeEvent {
169
- path: beads_file.to_string_lossy().to_string(),
170
- change_type: change_type.to_string(),
171
- };
172
-
173
- info!("File change detected: {:?}", file_event);
174
-
175
- let sse_event = Event::default()
176
- .data(serde_json::to_string(&file_event).unwrap_or_default());
177
-
178
- // If send fails, client disconnected
179
- if tx.send(Ok(sse_event)).await.is_err() {
180
- info!("Client disconnected, stopping watcher");
181
- break;
182
- }
183
- }
184
-
185
- // Watcher is automatically dropped and cleaned up here
186
- info!("File watcher stopped");
187
- Ok(())
188
- }
189
-
190
- #[cfg(test)]
191
- mod tests {
192
- use super::*;
193
-
194
- #[test]
195
- fn test_file_change_event_serialization() {
196
- let event = FileChangeEvent {
197
- path: "/test/path".to_string(),
198
- change_type: "modified".to_string(),
199
- };
200
- let json = serde_json::to_string(&event).unwrap();
201
- assert!(json.contains("\"path\":\"/test/path\""));
202
- assert!(json.contains("\"type\":\"modified\""));
203
- }
204
-
205
- #[test]
206
- fn test_watch_params_deserialization() {
207
- let params: WatchParams =
208
- serde_json::from_str(r#"{"path": "/test/project"}"#).unwrap();
209
- assert_eq!(params.path, "/test/project");
210
- }
211
- }
@@ -1,101 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- @layer base {
6
- :root {
7
- --background: 0 0% 100%;
8
- --foreground: 240 10% 3.9%;
9
- --card: 0 0% 100%;
10
- --card-foreground: 240 10% 3.9%;
11
- --popover: 0 0% 100%;
12
- --popover-foreground: 240 10% 3.9%;
13
- --primary: 240 5.9% 10%;
14
- --primary-foreground: 0 0% 98%;
15
- --secondary: 240 4.8% 95.9%;
16
- --secondary-foreground: 240 5.9% 10%;
17
- --muted: 240 4.8% 95.9%;
18
- --muted-foreground: 240 3.8% 46.1%;
19
- --accent: 240 4.8% 95.9%;
20
- --accent-foreground: 240 5.9% 10%;
21
- --destructive: 0 84.2% 60.2%;
22
- --destructive-foreground: 0 0% 98%;
23
- --border: 240 5.9% 90%;
24
- --input: 240 5.9% 90%;
25
- --ring: 240 5.9% 10%;
26
- --chart-1: 12 76% 61%;
27
- --chart-2: 173 58% 39%;
28
- --chart-3: 197 37% 24%;
29
- --chart-4: 43 74% 66%;
30
- --chart-5: 27 87% 67%;
31
- --radius: 0.5rem;
32
-
33
- /* Beads Status colors (HSL) */
34
- --status-open: 217 91% 60%;
35
- --status-in_progress: 38 92% 50%;
36
- --status-inreview: 271 81% 56%;
37
- --status-closed: 142 71% 45%;
38
-
39
- /* Beads Priority colors (HSL) */
40
- --priority-p0: 0 84% 60%;
41
- --priority-p1: 25 95% 53%;
42
- --priority-p2: 240 5% 65%;
43
- --priority-p3: 240 5% 84%;
44
- --priority-p4: 240 5% 84%;
45
-
46
- /* Beads Accents (HSL) */
47
- --blocked: 0 84% 60%;
48
- --branch: 142 76% 36%;
49
-
50
- /* roiui Card CSS Variables */
51
- --shadow-border-stack: 0 1px 2px hsl(var(--foreground) / 0.04);
52
- --mix-card-33-bg: hsl(var(--card) / 0.33);
53
- --ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
54
- --background-muted: hsl(var(--muted));
55
- }
56
-
57
- .dark {
58
- --background: 240 10% 3.9%;
59
- --foreground: 0 0% 98%;
60
- --card: 240 10% 3.9%;
61
- --card-foreground: 0 0% 98%;
62
- --popover: 240 10% 3.9%;
63
- --popover-foreground: 0 0% 98%;
64
- --primary: 0 0% 98%;
65
- --primary-foreground: 240 5.9% 10%;
66
- --secondary: 240 3.7% 15.9%;
67
- --secondary-foreground: 0 0% 98%;
68
- --muted: 240 3.7% 15.9%;
69
- --muted-foreground: 240 5% 64.9%;
70
- --accent: 240 3.7% 15.9%;
71
- --accent-foreground: 0 0% 98%;
72
- --destructive: 0 62.8% 30.6%;
73
- --destructive-foreground: 0 0% 98%;
74
- --border: 240 3.7% 15.9%;
75
- --input: 240 3.7% 15.9%;
76
- --ring: 240 4.9% 83.9%;
77
- --chart-1: 220 70% 50%;
78
- --chart-2: 160 60% 45%;
79
- --chart-3: 30 80% 55%;
80
- --chart-4: 280 65% 60%;
81
- --chart-5: 340 75% 55%;
82
- }
83
- }
84
-
85
- @layer base {
86
- * {
87
- @apply border-border;
88
- }
89
- body {
90
- @apply bg-background text-foreground;
91
- font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
92
- }
93
- }
94
-
95
- .font-heading {
96
- font-family: var(--font-space-grotesk), system-ui, sans-serif;
97
- }
98
-
99
- .font-project-name {
100
- font-family: var(--font-plus-jakarta), system-ui, sans-serif;
101
- }
@@ -1,36 +0,0 @@
1
- import type { Metadata } from 'next';
2
- import { Space_Grotesk, Plus_Jakarta_Sans } from 'next/font/google';
3
- import { Toaster } from '@/components/ui/toaster';
4
- import './globals.css';
5
-
6
- const spaceGrotesk = Space_Grotesk({
7
- subsets: ['latin'],
8
- display: 'swap',
9
- variable: '--font-space-grotesk',
10
- });
11
-
12
- const plusJakartaSans = Plus_Jakarta_Sans({
13
- subsets: ['latin'],
14
- display: 'swap',
15
- variable: '--font-plus-jakarta',
16
- });
17
-
18
- export const metadata: Metadata = {
19
- title: 'Beads',
20
- description: 'Kanban interface for beads - git-backed distributed issue tracker',
21
- };
22
-
23
- export default function RootLayout({
24
- children,
25
- }: {
26
- children: React.ReactNode;
27
- }) {
28
- return (
29
- <html lang="en" className={`dark ${spaceGrotesk.variable} ${plusJakartaSans.variable}`}>
30
- <body className="flex min-h-screen flex-col bg-background antialiased">
31
- <div className="flex-1">{children}</div>
32
- <Toaster />
33
- </body>
34
- </html>
35
- );
36
- }