A deployable markdown editor that connects with your self hosted files and lets you edit in a beautiful interface
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add user_repos table migration and related backend models and queries

+285 -15
+51
.opencode/plans/context.md
··· 1 + # Project Context 2 + 3 + ## Project Structure 4 + - **Root:** 5 + - `backend/`: Go backend service. 6 + - `frontend/`: Astro + React frontend. 7 + - `Makefile`: Build commands. 8 + - `docker-compose.yml`: Container orchestration. 9 + 10 + ## Backend (`/backend`) 11 + - **Language:** Go 12 + - **Framework:** Chi (Router) 13 + - **Database:** SQLite (with `modernc.org/sqlite` driver) 14 + - **Authentication:** Goth (GitHub OAuth) 15 + - **Key Directories:** 16 + - `internal/api/`: Handlers and routing. 17 + - `router.go`: Defines API routes. 18 + - `handlers/`: Contains logic for Auth, Repos, etc. 19 + - `internal/database/`: Database connection, models, and queries. 20 + - `models.go`: Struct definitions (`User`, `AuthToken`, `BranchState`, `DraftContent`). 21 + - `queries.go`: SQL queries. 22 + - `migrations/`: SQL migration files. 23 + - `internal/connectors/`: External API integrations (GitHub). 24 + - `github.go`: Handles GitHub API calls (listing repos, files). 25 + 26 + ## Frontend (`/frontend`) 27 + - **Framework:** Astro (Static Site Generation / Server Side Rendering) with React islands. 28 + - **Styling:** Tailwind CSS. 29 + - **State Management:** React Query (TanStack Query). 30 + - **Key Directories:** 31 + - `src/pages/`: 32 + - `index.astro`: Landing page. 33 + - `dashboard.astro`: Main application entry point. 34 + - `src/components/dashboard/`: 35 + - `DashboardApp.tsx`: Main React component for the dashboard. 36 + - `SetupWizard.tsx`: Component for selecting repository and folder (To be refactored). 37 + - `FileTree.tsx`: Displays repository file structure. 38 + - `src/lib/api/`: API client and endpoints. 39 + - `repos.ts`: Calls to backend repo endpoints. 40 + 41 + ## Current Functionality 42 + 1. **Auth:** GitHub OAuth flow via `/api/auth/github/login`. 43 + 2. **Repo Selection:** Users select a repo and optional folder path via `SetupWizard`. 44 + 3. **File Listing:** Fetches file tree from GitHub via Backend. Currently shows all folders, even empty ones. 45 + 4. **Editing:** Markdown editing (implied, likely using TipTap or similar based on `node_modules`). 46 + 47 + ## Proposed Changes Context 48 + - **Last Repo Persistence:** Currently, the app doesn't remember the selected repo across sessions. We need to add `last_repo` to the `User` model. 49 + - **Folder Selection:** The user finds the folder selection step unnecessary. We will enforce root (`""`) as the default. 50 + - **Empty Folders:** The file tree currently is "dumb" and shows the full structure. We need to make the backend filtering "smart" to only return relevant paths. 51 + - **Navigation:** The dashboard currently handles both setup and editing in one view. Splitting this into `/select-repo` and `/dashboard` will improve UX and browser history management.
+54
.opencode/plans/plan.md
··· 1 + # Implementation Plan 2 + 3 + ## 1. Save & Load Last Repo (Backend & Database) 4 + **Objective:** Store the user's last accessed repository in the database and return it upon login. 5 + 6 + ### Database Schema 7 + - [ ] Create a new migration `backend/internal/database/migrations/002_add_last_repo.sql`: 8 + - Add `last_repo` column (TEXT) to the `users` table. 9 + - [ ] Update `backend/internal/database/migrations.go` to ensure the new migration file is embedded and executed. 10 + 11 + ### Backend Logic 12 + - [ ] Update `backend/internal/database/models.go`: 13 + - Add `LastRepo string` to the `User` struct. 14 + - [ ] Update `backend/internal/database/queries.go`: 15 + - Update `GetUserByID` query and scan. 16 + - Update `GetUserByGithubID` query and scan. 17 + - Update `CreateUser` query and scan. 18 + - Add `UpdateUserRepo(userID int, repoName string)` function. 19 + - [ ] Update `backend/internal/api/handlers/auth.go`: 20 + - In `GetCurrentUser`, include `last_repo` in the response. 21 + - Create `UpdateUserRepo` handler to handle `POST /api/user/repo`. 22 + - [ ] Update `backend/internal/api/router.go`: 23 + - Register the new route `POST /api/user/repo`. 24 + 25 + ## 2. Remove Folder Selection Step (Frontend) 26 + **Objective:** Simplify the setup wizard to skip the folder selection step and default to root. 27 + 28 + ### Frontend Logic 29 + - [ ] Modify `frontend/src/components/dashboard/SetupWizard.tsx`: 30 + - Remove the "Step 2" (Folder Configuration) UI and logic. 31 + - Change "Next" button in Step 1 to "Complete Setup". 32 + - On submission, default the folder path to `""` (root). 33 + 34 + ## 3. Filter Empty Folders (Backend) 35 + **Objective:** Only show folders that actually contain markdown files (recursively). 36 + 37 + ### Backend Logic 38 + - [ ] Modify `backend/internal/connectors/github.go`: 39 + - In `ListFiles`: 40 + - Implement logic to check if a directory or its subdirectories contain matching files (based on extensions). 41 + - If a directory is empty (contains no matching files), exclude it from the returned `FileNode` structure. 42 + 43 + ## 4. Separate Repo Selection URL (Frontend Routing) 44 + **Objective:** Create a distinct URL for repo selection to support browser navigation. 45 + 46 + ### Frontend Architecture 47 + - [ ] Create `frontend/src/pages/select-repo.astro`: 48 + - Migrate `SetupWizard` component usage here. 49 + - [ ] Update `frontend/src/pages/dashboard.astro` & `DashboardApp.tsx`: 50 + - Check for `last_repo` in user data or URL params. 51 + - If no repo is active, redirect to `/select-repo`. 52 + - If repo is active, show the `EditorContainer`. 53 + - [ ] Update Sidebar Navigation: 54 + - "Change repository" button should link to `/select-repo`.
+30
backend/internal/api/handlers/auth.go
··· 242 242 } 243 243 244 244 // Logout logs out the current user 245 + func (h *AuthHandler) UpdateLastRepo(w http.ResponseWriter, r *http.Request) { 246 + var payload struct { 247 + LastRepo string `json:"last_repo"` 248 + } 249 + 250 + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { 251 + http.Error(w, "Invalid request payload", http.StatusBadRequest) 252 + return 253 + } 254 + 255 + session, err := auth.GetSession(r) 256 + if err != nil { 257 + http.Error(w, "Unauthorized", http.StatusUnauthorized) 258 + return 259 + } 260 + 261 + userID, ok := auth.GetUserID(session) 262 + if !ok { 263 + http.Error(w, "Unauthorized", http.StatusUnauthorized) 264 + return 265 + } 266 + 267 + if err := h.db.UpdateUserRepo(userID, payload.LastRepo); err != nil { 268 + http.Error(w, "Failed to update last repository", http.StatusInternalServerError) 269 + return 270 + } 271 + 272 + w.WriteHeader(http.StatusNoContent) 273 + } 274 + 245 275 func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { 246 276 session, err := auth.GetSession(r) 247 277 if err != nil {
+1
backend/internal/api/router.go
··· 60 60 61 61 r.Get("/api/auth/user", authHandler.GetCurrentUser) 62 62 r.Post("/api/auth/logout", authHandler.Logout) 63 + r.Post("/api/auth/last-repo", authHandler.UpdateLastRepo) 63 64 64 65 // Repository routes 65 66 r.Get("/api/repos", repoHandler.ListRepositories)
+18 -7
backend/internal/database/migrations.go
··· 13 13 func (db *DB) RunMigrations() error { 14 14 log.Println("Running database migrations...") 15 15 16 - // Read and execute the initial migration 17 - sqlBytes, err := migrationFiles.ReadFile("migrations/001_initial.sql") 16 + // Iterate over all migration files and execute them in order 17 + entries, err := migrationFiles.ReadDir("migrations") 18 18 if err != nil { 19 - return fmt.Errorf("failed to read migration file: %w", err) 19 + return fmt.Errorf("failed to read migrations directory: %w", err) 20 20 } 21 21 22 - // Execute the migration 23 - if _, err := db.Exec(string(sqlBytes)); err != nil { 24 - return fmt.Errorf("failed to execute migration: %w", err) 22 + for _, entry := range entries { 23 + if entry.IsDir() { 24 + continue 25 + } 26 + 27 + log.Printf("Running migration: %s", entry.Name()) 28 + sqlBytes, err := migrationFiles.ReadFile("migrations/" + entry.Name()) 29 + if err != nil { 30 + return fmt.Errorf("failed to read migration file %s: %w", entry.Name(), err) 31 + } 32 + 33 + if _, err := db.Exec(string(sqlBytes)); err != nil { 34 + return fmt.Errorf("failed to execute migration %s: %w", entry.Name(), err) 35 + } 25 36 } 26 37 27 - log.Println("Database migrations completed successfully") 38 + log.Println("All database migrations completed successfully") 28 39 return nil 29 40 }
+12
backend/internal/database/migrations/20260214_create_user_repos_table.sql
··· 1 + -- Create table 'user_repos' to manage repositories for each user 2 + CREATE TABLE user_repos ( 3 + id INTEGER PRIMARY KEY AUTOINCREMENT, 4 + user_id INTEGER NOT NULL, 5 + repo_name TEXT NOT NULL, 6 + repo_link TEXT NOT NULL, 7 + last_used_at DATETIME NOT NULL, 8 + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 + updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 10 + 11 + FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE 12 + );
+12
backend/internal/database/models.go
··· 11 11 AvatarURL string `json:"avatar_url"` 12 12 CreatedAt time.Time `json:"created_at"` 13 13 UpdatedAt time.Time `json:"updated_at"` 14 + LastRepo string `json:"last_repo"` 15 + } 16 + 17 + // UserRepo represents a repository associated with a user 18 + type UserRepo struct { 19 + ID int `json:"id"` 20 + UserID int `json:"user_id"` 21 + RepoName string `json:"repo_name"` 22 + RepoLink string `json:"repo_link"` 23 + LastUsedAt time.Time `json:"last_used_at"` 24 + CreatedAt time.Time `json:"created_at"` 25 + UpdatedAt time.Time `json:"updated_at"` 14 26 } 15 27 16 28 // AuthToken represents an OAuth token for a provider
+81 -3
backend/internal/database/queries.go
··· 3 3 import ( 4 4 "database/sql" 5 5 "fmt" 6 + "time" 6 7 ) 7 8 8 9 // CreateUser creates a new user or updates if exists 9 10 func (db *DB) CreateUser(user *User) error { 10 11 query := ` 11 - INSERT INTO users (github_id, username, email, avatar_url, updated_at) 12 + INSERT INTO users (github_id, username, email, avatar_url, lastRepo, updated_at) 12 13 VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP) 13 14 ON CONFLICT(github_id) DO UPDATE SET 14 15 username = excluded.username, 15 16 email = excluded.email, 16 17 avatar_url = excluded.avatar_url, 18 + lastRepo = excluded.lastRepo, 17 19 updated_at = CURRENT_TIMESTAMP 18 20 RETURNING id, created_at, updated_at 19 21 ` ··· 26 28 func (db *DB) GetUserByGithubID(githubID int) (*User, error) { 27 29 user := &User{} 28 30 query := ` 29 - SELECT id, github_id, username, email, avatar_url, created_at, updated_at 31 + SELECT id, github_id, username, email, avatar_url, lastRepo, created_at, updated_at 30 32 FROM users 31 33 WHERE github_id = ? 32 34 ` ··· 55 57 func (db *DB) GetUserByID(id int) (*User, error) { 56 58 user := &User{} 57 59 query := ` 58 - SELECT id, github_id, username, email, avatar_url, created_at, updated_at 60 + SELECT id, github_id, username, email, avatar_url, lastRepo, created_at, updated_at 59 61 FROM users 60 62 WHERE id = ? 61 63 ` ··· 254 256 } 255 257 256 258 // DeleteBranchState deletes branch state for a repository 259 + // UpdateUserRepo updates the last repository selected by a user 260 + func (db *DB) UpdateUserRepo(userID int, lastRepo string) error { 261 + query := ` 262 + UPDATE users 263 + SET lastRepo = ?, updated_at = CURRENT_TIMESTAMP 264 + WHERE id = ? 265 + ` 266 + 267 + _, err := db.Exec(query, lastRepo, userID) 268 + if err != nil { 269 + return fmt.Errorf("failed to update user's last repository: %w", err) 270 + } 271 + 272 + return nil 273 + } 274 + 275 + // InsertUserRepo adds a new repository to the user_repos table 276 + func (db *DB) InsertUserRepo(userID int, repoName, repoLink string, lastUsedAt time.Time) error { 277 + query := ` 278 + INSERT INTO user_repos (user_id, repo_name, repo_link, last_used_at) 279 + VALUES (?, ?, ?, ?) 280 + ` 281 + 282 + _, err := db.Exec(query, userID, repoName, repoLink, lastUsedAt) 283 + if err != nil { 284 + return fmt.Errorf("failed to insert user repo: %w", err) 285 + } 286 + 287 + return nil 288 + } 289 + 290 + // UpdateLastUsedAt updates the last_used_at for a specific repo 291 + func (db *DB) UpdateLastUsedAt(userID int, repoName string, lastUsedAt time.Time) error { 292 + query := ` 293 + UPDATE user_repos 294 + SET last_used_at = ?, updated_at = CURRENT_TIMESTAMP 295 + WHERE user_id = ? AND repo_name = ? 296 + ` 297 + 298 + _, err := db.Exec(query, lastUsedAt, userID, repoName) 299 + if err != nil { 300 + return fmt.Errorf("failed to update last_used_at: %w", err) 301 + } 302 + 303 + return nil 304 + } 305 + 306 + // GetUserRepos retrieves all repositories for a user 307 + func (db *DB) GetUserRepos(userID int) ([]UserRepo, error) { 308 + query := ` 309 + SELECT id, user_id, repo_name, repo_link, last_used_at, created_at, updated_at 310 + FROM user_repos 311 + WHERE user_id = ? 312 + ` 313 + 314 + rows, err := db.Query(query, userID) 315 + if err != nil { 316 + return nil, fmt.Errorf("failed to get user repos: %w", err) 317 + } 318 + defer rows.Close() 319 + 320 + var repos []UserRepo 321 + for rows.Next() { 322 + var repo UserRepo 323 + if err := rows.Scan( 324 + &repo.ID, &repo.UserID, &repo.RepoName, &repo.RepoLink, 325 + &repo.LastUsedAt, &repo.CreatedAt, &repo.UpdatedAt, 326 + ); err != nil { 327 + return nil, fmt.Errorf("failed to scan row: %w", err) 328 + } 329 + repos = append(repos, repo) 330 + } 331 + 332 + return repos, nil 333 + } 334 + 257 335 func (db *DB) DeleteBranchState(userID int, repoFullName string) error { 258 336 query := ` 259 337 DELETE FROM branch_states
+24 -3
frontend/bun.lock
··· 27 27 "react": "^19.2.4", 28 28 "react-dom": "^19.2.4", 29 29 "remark": "^15.0.1", 30 + "remark-html": "^16.0.1", 30 31 "remark-parse": "^11.0.0", 31 32 "remark-stringify": "^11.0.0", 32 33 "sonner": "^2.0.7", 33 34 "tailwind-merge": "^3.4.0", 34 35 "tailwindcss": "^3.4.0", 36 + "tiptap-markdown": "^0.9.0", 35 37 "turndown": "^7.2.2", 36 38 "unified": "^11.0.5", 39 + }, 40 + "devDependencies": { 41 + "@types/turndown": "^5.0.6", 37 42 }, 38 43 }, 39 44 }, ··· 374 379 375 380 "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], 376 381 377 - "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], 382 + "@types/linkify-it": ["@types/linkify-it@3.0.5", "", {}, "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw=="], 378 383 379 - "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], 384 + "@types/markdown-it": ["@types/markdown-it@13.0.9", "", { "dependencies": { "@types/linkify-it": "^3", "@types/mdurl": "^1" } }, "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw=="], 380 385 381 386 "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], 382 387 383 - "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], 388 + "@types/mdurl": ["@types/mdurl@1.0.5", "", {}, "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA=="], 384 389 385 390 "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 386 391 ··· 389 394 "@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="], 390 395 391 396 "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], 397 + 398 + "@types/turndown": ["@types/turndown@5.0.6", "", {}, "sha512-ru00MoyeeouE5BX4gRL+6m/BsDfbRayOskWqUvh7CLGW+UXxHQItqALa38kKnOiZPqJrtzJUgAC2+F0rL1S4Pg=="], 392 399 393 400 "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], 394 401 ··· 626 633 627 634 "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], 628 635 636 + "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="], 637 + 629 638 "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], 630 639 631 640 "hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="], ··· 699 708 "magicast": ["magicast@0.5.1", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "source-map-js": "^1.2.1" } }, "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw=="], 700 709 701 710 "markdown-it": ["markdown-it@14.1.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg=="], 711 + 712 + "markdown-it-task-lists": ["markdown-it-task-lists@2.1.1", "", {}, "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="], 702 713 703 714 "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], 704 715 ··· 950 961 951 962 "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], 952 963 964 + "remark-html": ["remark-html@16.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "hast-util-sanitize": "^5.0.0", "hast-util-to-html": "^9.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0" } }, "sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ=="], 965 + 953 966 "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], 954 967 955 968 "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], ··· 1021 1034 "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], 1022 1035 1023 1036 "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 1037 + 1038 + "tiptap-markdown": ["tiptap-markdown@0.9.0", "", { "dependencies": { "@types/markdown-it": "^13.0.7", "markdown-it": "^14.1.0", "markdown-it-task-lists": "^2.1.1", "prosemirror-markdown": "^1.11.1" }, "peerDependencies": { "@tiptap/core": "^3.0.1" } }, "sha512-dKLQ9iiuGNgrlGVjrNauF/UBzWu4LYOx5pkD0jNkmQt/GOwfCJsBuzZTsf1jZ204ANHOm572mZ9PYvGh1S7tpQ=="], 1024 1039 1025 1040 "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 1026 1041 ··· 1144 1159 1145 1160 "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], 1146 1161 1162 + "prosemirror-markdown/@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], 1163 + 1147 1164 "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1148 1165 1149 1166 "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], ··· 1155 1172 "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 1156 1173 1157 1174 "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], 1175 + 1176 + "prosemirror-markdown/@types/markdown-it/@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], 1177 + 1178 + "prosemirror-markdown/@types/markdown-it/@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], 1158 1179 1159 1180 "unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], 1160 1181
+1 -1
frontend/src/components/dashboard/RepoSelector.tsx
··· 32 32 return ( 33 33 <div className="p-4 border-b border-gray-200 space-y-3"> 34 34 <div className="flex items-center justify-between"> 35 - <label className="text-sm font-semibold text-gray-900">Select Repository</label> 35 + <label className="text-sm font-semibold text-gray-900">Select Primary Repository</label> 36 36 <select 37 37 value={sortBy} 38 38 onChange={(e) => setSortBy(e.target.value as any)}
+1 -1
frontend/src/components/dashboard/SetupWizard.tsx
··· 125 125 <div className="space-y-6"> 126 126 <div> 127 127 <h2 className="text-xl font-bold text-gray-900 mb-4"> 128 - Select Your Repository 128 + Select Your primary Repository 129 129 </h2> 130 130 <p className="text-sm text-gray-600 mb-6"> 131 131 Choose the GitHub repository where your blog posts are stored.