search and/or read your saved and liked bluesky posts
wails go svelte sqlite desktop bluesky
4
fork

Configure Feed

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

docs: add changelog and GitHub Actions release workflow, and update the development roadmap for v0.2.0

+196 -56
+104
.github/workflows/release.yml
··· 1 + name: Release 2 + 3 + on: 4 + push: 5 + tags: 6 + - "v*" 7 + 8 + permissions: 9 + contents: write 10 + 11 + jobs: 12 + build: 13 + strategy: 14 + matrix: 15 + include: 16 + - os: ubuntu-latest 17 + platform: linux/amd64 18 + output_name: bsky-browser-gui-linux-amd64 19 + - os: macos-latest 20 + platform: darwin/universal 21 + output_name: bsky-browser-gui-macos-universal 22 + - os: windows-latest 23 + platform: windows/amd64 24 + output_name: bsky-browser-gui-windows-amd64 25 + 26 + runs-on: ${{ matrix.os }} 27 + steps: 28 + - name: Checkout 29 + uses: actions/checkout@v4 30 + 31 + - name: Setup Go 32 + uses: actions/setup-go@v5 33 + with: 34 + go-version: "1.25" 35 + 36 + - name: Setup Node.js 37 + uses: actions/setup-node@v4 38 + with: 39 + node-version: "22" 40 + 41 + - name: Setup pnpm 42 + uses: pnpm/action-setup@v4 43 + with: 44 + run_install: false 45 + 46 + - name: Install Linux dependencies (GTK + WebKit) 47 + if: runner.os == 'Linux' 48 + run: | 49 + sudo apt-get update 50 + sudo apt-get install -y \ 51 + libgtk-3-dev \ 52 + libwebkit2gtk-4.0-dev \ 53 + build-essential \ 54 + pkg-config 55 + 56 + - name: Install Wails CLI 57 + run: go install github.com/wailsapp/wails/v2/cmd/wails@latest 58 + 59 + - name: Install frontend dependencies 60 + working-directory: frontend 61 + run: pnpm install 62 + 63 + - name: Build 64 + run: wails build -platform ${{ matrix.platform }} 65 + 66 + - name: Archive (Linux) 67 + if: runner.os == 'Linux' 68 + working-directory: build/bin 69 + run: tar czf ${{ matrix.output_name }}.tar.gz bsky-browser-gui 70 + 71 + - name: Archive (macOS) 72 + if: runner.os == 'macOS' 73 + working-directory: build/bin 74 + run: zip -r ${{ matrix.output_name }}.zip bsky-browser-gui.app 75 + 76 + - name: Archive (Windows) 77 + if: runner.os == 'Windows' 78 + working-directory: build/bin 79 + run: Compress-Archive -Path bsky-browser-gui.exe -DestinationPath ${{ matrix.output_name }}.zip 80 + 81 + - name: Upload artifact 82 + uses: actions/upload-artifact@v4 83 + with: 84 + name: ${{ matrix.output_name }} 85 + path: | 86 + build/bin/*.tar.gz 87 + build/bin/*.zip 88 + 89 + release: 90 + needs: build 91 + runs-on: ubuntu-latest 92 + steps: 93 + - name: Download all artifacts 94 + uses: actions/download-artifact@v4 95 + with: 96 + merge-multiple: true 97 + 98 + - name: Create GitHub Release 99 + uses: softprops/action-gh-release@v2 100 + with: 101 + generate_release_notes: true 102 + files: | 103 + *.tar.gz 104 + *.zip
+16
CHANGELOG.md
··· 1 + # Changelog 2 + 3 + ## 2026-03-15 4 + 5 + ### Added 6 + 7 + - Persistence via SQLite with WAL mode and FTS5 search 8 + - Loopback OAuth flow, token persistence, automatic refresh on startup 9 + - Concurrent bookmark and like fetching, batch insert, facet population, 10 + real-time progress bar via Wails events 11 + - FTS5 full-text search with BM25 ranking, source filtering (All/Saved/Liked), 12 + sortable columns, and row click to open in browser 13 + - Real-time log panel with terminal styling, auto-scroll with scroll-lock toggle, 14 + and level filtering (Debug/Info/Warn/Error) 15 + - Keyboard Shortcuts -> `Cmd+K` focus search, `Cmd+R` refresh, `Cmd+L` toggle 16 + log viewer
+76 -56
TODO.md
··· 1 - # Wails Desktop App — Roadmap 1 + # v0.2.0 Roadmap 2 2 3 - ## Milestone 1 — Scaffold & Tooling 3 + ## Milestone 1 — Multi-Account Database Architecture 4 4 5 - - [x] Install Tailwind v4 (`tailwindcss`, `@tailwindcss/vite`) and wire `vite.config.ts` 6 - - [x] Install Fontsource packages (`@fontsource/jetbrains-mono`, `@fontsource-variable/geist`, `@fontsource-variable/lora`) 7 - - [x] Create `frontend/src/index.css` with `@import "tailwindcss"` and `@theme` tokens (palette, fonts) 8 - - [x] Put font CSS imports in `App.svelte` 9 - - [x] Set up `Taskfile.yml` for build tasks 10 - - [x] Verify `wails dev` hot-reloads a "Hello World" page with correct fonts and theme 5 + - [ ] Create `DatabaseManager` struct to replace global `db *sql.DB` (holds shared DB + per-account DB) 6 + - [ ] Implement per-account database path resolution: `<config_dir>/bsky-browser/accounts/<DID>/bsky-browser.db` 7 + - [ ] Migrate auth queries (`GetAuth`, `UpsertAuth`, `GetAuthByDID`) to use shared DB connection 8 + - [ ] Migrate post queries (`InsertPost`, `SearchPosts`, `CountPosts`, `PostExists`) to use per-account DB connection 9 + - [ ] Update `Open()` to initialize both shared and per-account databases with migrations 10 + - [ ] Update `app.startup()` to auto-select the most recently updated account and open its database 11 + - [ ] Add migration `001_add_active_account.sql` for tracking active account in shared DB 12 + - [ ] Tests: `DatabaseManager` open/close, account switching with multiple temp databases 11 13 12 - ## Milestone 2 — Database Layer 14 + ## Milestone 2 — Account Switcher Service & UI 13 15 14 - - [x] Implement `database.go` — `Open()`, `Close()`, embedded migrations via `//go:embed` 15 - - [x] Copy existing migrations from CLI (`000`–`003`) and add `004_add_facets_column.sql` (`ALTER TABLE posts ADD COLUMN facets TEXT`) 16 - - [x] Enable WAL mode (`PRAGMA journal_mode=WAL`) on connection open 17 - - [x] Implement `models.go` — `Post`, `Auth`, `SearchResult` structs (add `Facets` field to `Post`) 18 - - [x] Implement `PostExists`, `InsertPost`, `UpsertAuth`, `GetAuth`, `SearchPosts`, `CountPosts` 19 - - [x] Verify the desktop app and CLI can read/write the same database concurrently 16 + - [ ] Implement `AccountService` struct with Wails binding 17 + - [ ] `ListAccounts()` — query all rows from shared `auth` table 18 + - [ ] `SwitchAccount(did)` — close current per-account DB, open target, emit `account:switched` event 19 + - [ ] `RemoveAccount(did)` — delete auth row and account database directory 20 + - [ ] `GetActiveAccount()` — return current account info 21 + - [ ] Update `AuthService.Login()` to create per-account DB directory on new login 22 + - [ ] Frontend: account switcher dropdown in header with handle list 23 + - [ ] Frontend: "Add Account" and "Remove Account" actions 24 + - [ ] Frontend: listen for `account:switched` to reset search/post state 25 + - [ ] Tests: `ListAccounts`, `SwitchAccount`, `RemoveAccount` with temp dirs 20 26 21 - ## Milestone 3 — Authentication 27 + ## Milestone 3 — Profile View & Author Feed 22 28 23 - - [x] Implement `AuthService` struct with Wails service binding 24 - - [x] `Login(handle)` — loopback OAuth via `oauth.NewLocalhostConfig`, persist tokens to shared DB 25 - - [x] `Whoami(force)` — load auth from DB, optionally resolve handle from DID 26 - - [x] `IsAuthenticated()` — check for valid auth row 27 - - [x] Automatic token refresh on `OnStartup` lifecycle hook 28 - - [x] Frontend: login view with handle input, "Login" button, and status display 29 + - [ ] Add `ProfileView` model struct to `models.go` 30 + - [ ] Implement `ProfileService` with Wails binding 31 + - [ ] `GetProfile()` — call `bsky.ActorGetProfile` with active account DID 32 + - [ ] `GetAuthorFeed(filter, cursor, limit)` — call `bsky.FeedGetAuthorFeed` with pagination 33 + - [ ] Frontend: `ProfileView.svelte` component with profile card (avatar, name, bio, stats) 34 + - [ ] Frontend: tabbed author feed below profile card (Posts / Replies / Media filters) 35 + - [ ] Frontend: infinite scroll pagination using cursor 36 + - [ ] Frontend: navigate to profile view from header handle/avatar click 29 37 30 - ## Milestone 4 — Indexing & Progress 38 + ## Milestone 4 — Search Own Posts 31 39 32 - - [x] Implement `IndexService` struct with Wails service binding 33 - - [x] `Refresh(limit)` — concurrent bookmark + like fetch, batch insert (port `RefreshAndIndex` logic) 34 - - [x] Populate `facets` column from `FeedPost.Facets` during `convertPostView` 35 - - [x] `IsIndexing()` — thread-safe boolean guard to prevent concurrent refreshes 36 - - [x] Emit Wails events: `index:started`, `index:progress`, `index:done` 37 - - [x] Frontend: "Refresh" button in header, optional limit input 38 - - [x] Frontend: bottom-pinned progress bar component driven by `index:*` events 40 + - [ ] Extend `SearchPosts()` with optional `authorDID` parameter 41 + - [ ] Update FTS5 query to add `AND p.author_did = ?` when author filter is active 42 + - [ ] Update `SearchService.Search()` to accept the author filter 43 + - [ ] Frontend: "My Posts" toggle/tab in SearchBar that constrains to active account DID 44 + - [ ] Tests: `SearchPosts` with author filter, mixed-author datasets 39 45 40 - ## Milestone 5 — Search & Data Table 46 + ## Milestone 5 — Post Composer 41 47 42 - - [x] Implement `SearchService` struct with Wails service binding 43 - - [x] `Search(query, source)` — FTS5 query with BM25 ranking and source filter 44 - - [x] `CountPosts()` — total indexed post count 45 - - [x] Frontend: search bar with query input and source filter (All / Saved / Liked segmented control) 46 - - [x] Frontend: tabbed data table component (Saved / Liked / All tabs) 47 - - [x] Columns: Author Handle, Text (truncated), Created At, ♥ Likes, 🔁 Reposts, 💬 Replies, Source 48 - - [x] Client-side column sorting (click header to toggle asc/desc) 49 - - [x] Row click → open post URL in default browser via `runtime.BrowserOpenURL` 48 + - [ ] Implement `PostService` struct with Wails binding 49 + - [ ] `CreatePost(text, replyTo)` — build `bsky.FeedPost` record, call `atproto.RepoCreateRecord` 50 + - [ ] Facet detection for mentions, links, and hashtags 51 + - [ ] Character limit enforcement (300 grapheme clusters) 52 + - [ ] Frontend: compose button in header 53 + - [ ] Frontend: compose modal with textarea, character counter, live facet preview 54 + - [ ] Frontend: optional reply-to context from PostDetailPanel 50 55 51 - ## Milestone 6 — Facets & Log Viewer 56 + ## Milestone 6 — Drafts 52 57 53 - - [x] Frontend: facet parser — convert UTF-8 byte offsets to JS string indices 54 - - [x] Frontend: facet renderer — links (`<a>`), mentions (`@handle`), hashtags (`#tag`) 55 - - [x] Integrate rendered facets into post text in data table rows 56 - - [x] Implement `LogService` — custom `io.Writer` that emits `log:line` events 57 - - [x] Wire `LogService` writer into `log.NewWithOptions` alongside file writer 58 - - [x] Frontend: log viewer panel with terminal-style dark background, monospace text 59 - - [x] Auto-scroll to bottom with scroll-lock toggle 60 - - [x] Level filter buttons: Debug, Info, Warn, Error 58 + - [ ] Add `drafts` table migration in per-account database 59 + - [ ] Implement `PostService.SaveDraft()`, `ListDrafts()`, `GetDraft()`, `DeleteDraft()` 60 + - [ ] `PublishDraft(id)` — create post and delete draft atomically 61 + - [ ] Frontend: "Save Draft" button in compose modal 62 + - [ ] Frontend: drafts panel with list view, click-to-edit, and delete 63 + - [ ] Frontend: draft count badge on drafts button 64 + - [ ] Tests: draft CRUD operations and publish flow 61 65 62 - ## Milestone 7 — Polish 66 + ## Milestone 7 — System Tray 67 + 68 + - [ ] Add `ra1phdd/systray-on-wails` dependency 69 + - [ ] Create tray icon asset (`build/tray_icon.png`, 22×22 monochrome) 70 + - [ ] Initialize system tray in `app.startup()` with menu items 71 + - [ ] Menu items: active account label, Quick Post, Refresh Posts, Show/Hide Window, Quit 72 + - [ ] Quick Post: focus window and open compose modal via Wails event 73 + - [ ] Refresh Posts: trigger `IndexService.Refresh(0)` from tray 74 + 75 + ## Milestone 8 — Go Test Coverage 76 + 77 + - [ ] `auth_service_test.go` — `IsAuthenticated`, `Whoami` with mocked DB state 78 + - [ ] `index_service_test.go` — `batchWriter`, `convertPostView`, `extractFacets`, `parsePostRecord` 79 + - [ ] `post_service_test.go` — draft CRUD, `CreatePost` record assembly (no network) 80 + - [ ] `account_service_test.go` — multi-account switching with temp directories 81 + - [ ] Expand `database_test.go` — `InsertPost` upsert, `CountPosts`, multi-account `UpsertAuth`, FTS5 ranking edge cases 82 + - [ ] Establish CI-friendly test target in `Taskfile.yml` (`task test`) 63 83 64 - - [x] Animations with svelte APIs (transition, animate) 65 - - [x] Empty state: show "No posts indexed" with prompt to refresh 66 - - [x] Error handling: toast/notification for network failures, auth expiry 67 - - [x] Keyboard shortcuts: `Cmd+K` focus search, `Cmd+R` refresh, `Cmd+L` toggle log viewer 68 - - [x] Window title and app icon (`build/appicon.png`) 69 - - [x] Production build verification (`wails build` → macOS `.app` bundle) 70 - - [~] README with build instructions, screenshots, and usage 84 + ## Milestone 9 — Polish & Integration 85 + 86 + - [ ] End-to-end smoke test: login → switch account → compose → post → verify in feed 87 + - [ ] Error handling: toast notifications for tray actions, compose failures, account switch errors 88 + - [ ] Loading states for profile view, author feed, and compose 89 + - [ ] Keyboard shortcut: `Cmd+N` open compose modal 90 + - [ ] Update README with Phase 2 features and screenshots