BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

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

docs: add initial specifications and task breakdown for MVP

+541 -10
+43
docs/specs/auth.md
··· 1 + # Auth & Account Management 2 + 3 + ## OAuth 2.1 Loopback Flow 4 + 5 + Uses `jacquard::oauth` with `LoopbackConfig` to authenticate: 6 + 7 + 1. User enters handle or DID 8 + 2. Resolve authorization server via `jacquard_oauth::resolver` 9 + 3. Build `AtprotoClientMetadata` with app identity 10 + 4. `OAuthClient` initiates PAR + DPoP flow 11 + 5. Loopback server captures redirect on `127.0.0.1:<port>` 12 + 6. Exchange code for tokens; `OAuthSession` manages refresh automatically 13 + 14 + No app passwords needed — full OAuth 2.1 with DPoP proof-of-possession but app passwords 15 + should be supported in dev environments. 16 + 17 + ## Multi-Account 18 + 19 + - SQLite table `accounts`: `did TEXT PK, handle TEXT, pds_url TEXT, active INTEGER` 20 + - Encrypted token storage via `jacquard_oauth::authstore` trait with a persistent implementation backed by SQLite + OS keychain (Tauri's `tauri-plugin-keychain` or raw `security-framework`) 21 + - Account switcher in sidebar — click to swap active session 22 + - Each account gets its own `OAuthSession` instance 23 + - Active account DID stored in app state; Tauri events notify frontend on switch 24 + 25 + ## UX Polish 26 + 27 + - Login form: `Motion` spring animation on the handle input shake for invalid input 28 + - Account switcher: `Presence` exit/enter animation when swapping active account avatar 29 + - Session expiry: inline re-auth prompt with gentle pulse animation, not a modal wall 30 + - Loading: skeleton shimmer on profile card while session restores 31 + 32 + ## Session Lifecycle 33 + 34 + - On launch: load stored sessions, attempt token refresh for active account 35 + - On token expiry: `jacquard::oauth` auto-refreshes via DPoP-bound refresh token 36 + - On refresh failure: prompt re-auth, mark account as expired in UI 37 + - Logout: revoke tokens, clear stored auth data 38 + 39 + ## at:// Deep Link Registration 40 + 41 + - Register `at` scheme via `tauri-plugin-deep-link` 42 + - On `at://` link open: parse URI, route to AT Explorer view 43 + - If app not running: launch, then navigate after session restore
+64
docs/specs/explorer.md
··· 1 + # AT Explorer (pds.ls-style) 2 + 3 + A built-in browser for AT Protocol data, inspired by [pds.ls](https://pds.ls/). 4 + This is the view that opens when handling `at://` URIs. 5 + 6 + ## Navigation Model 7 + 8 + URL-bar style input accepting: 9 + 10 + - `at://` URIs → route directly to record/collection/repo 11 + - Handles (`@user.bsky.social`) → resolve DID → show repo 12 + - DIDs (`did:plc:...`) → show repo 13 + - PDS URLs (`https://pds.example.com`) → list hosted repos 14 + 15 + ## Views 16 + 17 + ### PDS View 18 + 19 + - List accounts hosted on a PDS 20 + - Show PDS metadata (version, invite codes status) 21 + - Endpoint: `com.atproto.server.describeServer` 22 + 23 + ### Repository View 24 + 25 + - List all collections in a repo (e.g., `app.bsky.feed.post`, `app.bsky.feed.like`) 26 + - Show repo metadata: DID, handle, PDS URL 27 + - Endpoint: `com.atproto.repo.describeRepo`, `com.atproto.sync.getRepo` 28 + 29 + ### Collection View 30 + 31 + - Paginated list of records in a collection 32 + - Endpoint: `com.atproto.repo.listRecords` 33 + 34 + ### Record View 35 + 36 + - Full JSON display of a single record 37 + - Render known types nicely (posts → rich text, likes → linked post, follows → profile card) 38 + - Show CID, rkey, timestamps 39 + - Endpoint: `com.atproto.repo.getRecord` 40 + 41 + ## Additional Features 42 + 43 + - **Backlinks**: show records that reference the current record (requires relay/constellation) 44 + - **Jetstream live view**: stream new records in real-time via `jacquard::jetstream` 45 + - **CAR export**: download repo as CAR archive via `com.atproto.sync.getRepo` 46 + - **Moderation labels**: query and display labels via `com.atproto.label.queryLabels` 47 + - **Breadcrumb navigation**: `PDS > Repo > Collection > Record` with back/forward 48 + 49 + ## Keyboard Shortcuts 50 + 51 + | Key | Action | 52 + | ----------------- | ----------------------------------- | 53 + | `Cmd+L` | Focus explorer URL bar | 54 + | `Backspace` | Navigate up one level in breadcrumb | 55 + | `Cmd+[` / `Cmd+]` | Back / forward | 56 + 57 + ## UX Polish 58 + 59 + - View transitions: `Presence` crossfade when navigating between PDS → repo → collection → record 60 + - Jetstream live-tail: new records `Motion` slide-in from top with fade 61 + - JSON record view: syntax-highlighted with collapsible sections 62 + - Breadcrumb segments animate width via `Motion` on navigation 63 + - Skeleton screens for each view level while loading 64 + - Error inline with retry, not a blocking modal
+58
docs/specs/mvp.md
··· 1 + # Lazurite Desktop — MVP Spec 2 + 3 + A native desktop BlueSky/AT Protocol client built with **Tauri v2** (Rust) + **SolidJS**, focused on power-user features: multi-account, local semantic search, AT Protocol data exploration, and long-form content via standard.site lexicons. 4 + 5 + ## Architecture 6 + 7 + ```text 8 + ┌─────────────────────────────────────────────┐ 9 + │ SolidJS Frontend (WebView) │ 10 + │ ├─ Timeline / Feed views │ 11 + │ ├─ AT Explorer (pds.ls-style) │ 12 + │ ├─ Search UI (FTS + semantic) │ 13 + │ └─ Account switcher │ 14 + ├─────────────────────────────────────────────┤ 15 + │ Tauri IPC (Commands + Events) │ 16 + ├─────────────────────────────────────────────┤ 17 + │ Rust Backend │ 18 + │ ├─ jacquard — XRPC client + types │ 19 + │ ├─ jacquard::oauth — OAuth 2.1 loopback │ 20 + │ ├─ rusqlite + sqlite-vec — local storage │ 21 + │ ├─ fastembed — nomic-embed-text │ 22 + │ └─ tauri plugins — deep-link, sql, etc │ 23 + └─────────────────────────────────────────────┘ 24 + ``` 25 + 26 + ## Key Dependencies 27 + 28 + | Crate / Lib | Purpose | 29 + | ------------------------ | ---------------------------------------------------------------------- | 30 + | `jacquard` | AT Protocol XRPC client, zero-copy types, session management | 31 + | `jacquard::oauth` | OAuth 2.1 with DPoP, PKCE, PAR; loopback flow via `LoopbackConfig` | 32 + | `rusqlite` (bundled) | Local SQLite database | 33 + | `sqlite-vec` | Vector similarity search extension for SQLite | 34 + | `fastembed` | Local ONNX inference for `nomic-embed-text-v1.5` embeddings | 35 + | `tauri-plugin-deep-link` | Register `at://` URI scheme handler | 36 + | `tauri-plugin-sql` | Optional — frontend-side DB queries | 37 + | `solid-motionone` | Animation primitives (`Motion`, `Presence`) for SolidJS via Motion One | 38 + 39 + ## Cross-Cutting Concerns 40 + 41 + - **Theme**: dark/light mode synced with OS, applied globally 42 + - **Keyboard shortcuts**: Aeronaut-inspired, registered per-view (see individual specs) 43 + - **Error UX**: toast notifications for transient errors, inline retry for network failures 44 + - **Loading states**: skeleton screens for feeds/lists, spinners for actions 45 + - **Accessibility**: ARIA labels, keyboard focus management, screen reader support 46 + - **Animations** (`solid-motionone`): used throughout for transitions and micro-interactions (see individual specs for specifics) 47 + - **Auto-update**: `tauri-plugin-updater` checking GitHub Releases 48 + - **Packaging**: macOS code signing, notarization, DMG distribution 49 + 50 + ## Feature Modules 51 + 52 + Details in sub-specs: 53 + 54 + - [Authentication & Accounts](./auth.md) 55 + - [Timeline & Social](./timeline.md) 56 + - [AT Explorer](./explorer.md) 57 + - [Search & Embeddings](./search.md) 58 + - [standard.site Integration](./standard-site.md)
+76
docs/specs/search.md
··· 1 + # Search & Embeddings 2 + 3 + Local full-text + semantic search over the authenticated user's saved and liked posts. 4 + 5 + ## Data Pipeline 6 + 7 + 1. **Sync**: on login and periodically, fetch user's likes (`app.bsky.feed.getActorLikes`) and bookmarks. Paginate fully, store in SQLite. 8 + 2. **Index FTS**: insert post text into SQLite FTS5 virtual table for keyword search. 9 + 3. **Embed**: run post text through `fastembed` with `nomic-embed-text-v1.5` (768-dim). Store vectors in `sqlite-vec` virtual table. 10 + 4. **Incremental**: track cursor/last-seen; only process new posts on subsequent syncs. 11 + 12 + ## SQLite Schema 13 + 14 + ```sql 15 + -- Post storage 16 + CREATE TABLE posts ( 17 + uri TEXT PRIMARY KEY, 18 + cid TEXT NOT NULL, 19 + author_did TEXT NOT NULL, 20 + author_handle TEXT, 21 + text TEXT, 22 + created_at TEXT, 23 + indexed_at TEXT DEFAULT CURRENT_TIMESTAMP, 24 + json_record TEXT, -- full record JSON 25 + source TEXT NOT NULL -- 'like', 'bookmark', 'own' 26 + ); 27 + 28 + -- Full-text search 29 + CREATE VIRTUAL TABLE posts_fts USING fts5(text, uri UNINDEXED, content=posts, content_rowid=rowid); 30 + 31 + -- Vector embeddings 32 + CREATE VIRTUAL TABLE posts_vec USING vec0( 33 + uri TEXT PRIMARY KEY, 34 + embedding float[768] 35 + ); 36 + ``` 37 + 38 + ## Search Modes 39 + 40 + | Mode | How | 41 + | -------- | ----------------------------------------------------------------------------------------- | 42 + | Keyword | `SELECT * FROM posts_fts WHERE posts_fts MATCH ?` | 43 + | Semantic | Embed query → `SELECT * FROM posts_vec WHERE embedding MATCH ? ORDER BY distance LIMIT k` | 44 + | Hybrid | Run both, merge results by reciprocal rank fusion | 45 + 46 + ## Embedding Details 47 + 48 + - Model: `nomic-embed-text-v1.5` via `fastembed` (ONNX runtime, no GPU required) 49 + - Dimensions: 768 (or 256 with Matryoshka truncation for speed) 50 + - Batch embedding on sync; single embedding on search query 51 + - Model downloaded on first use, cached in Tauri app data dir 52 + 53 + ## Tauri Commands 54 + 55 + ```rs 56 + search_posts(query: String, mode: "keyword"|"semantic"|"hybrid", limit: u32) -> Vec<PostResult> 57 + sync_liked_posts(did: String) -> SyncStatus 58 + get_sync_status(did: String) -> SyncStatus 59 + ``` 60 + 61 + ## Keyboard Shortcuts 62 + 63 + | Key | Action | 64 + | -------- | ----------------------------------------------- | 65 + | `/` | Focus search bar from anywhere | 66 + | `Tab` | Cycle search mode (keyword → semantic → hybrid) | 67 + | `Escape` | Clear search / close results | 68 + 69 + ## UX Polish 70 + 71 + - Search results: staggered `Motion` fade-in list animation 72 + - Mode switcher: `Motion` sliding indicator underline between tabs 73 + - Sync status: animated progress bar during sync, `Presence` fade-out when complete 74 + - Highlighted keyword matches in result text 75 + - Model download: progress bar on first launch with percentage + ETA 76 + - Empty state: illustration with prompt when no posts synced yet
+43
docs/specs/standard-site.md
··· 1 + # Standard.site Integration 2 + 3 + Display long-form content for any handle using [standard.site](https://standard.site) lexicons. 4 + 5 + ## Lexicons 6 + 7 + | Lexicon | Purpose | 8 + | ---------------------------------- | -------------------------------------------------------------- | 9 + | `site.standard.publication` | Publication metadata: name, description, icon, base URL, theme | 10 + | `site.standard.document` | Individual document/post: title, content, metadata | 11 + | `site.standard.graph.subscription` | User subscriptions to publications | 12 + 13 + ## Feature: View Publications for a Handle 14 + 15 + 1. Given a handle, resolve DID 16 + 2. Query `com.atproto.repo.listRecords` for collection `site.standard.publication` 17 + 3. If found, display publication card (name, description, icon) 18 + 4. List documents via `site.standard.document` collection 19 + - Leaflet 20 + - PCKT 21 + - Offprint 22 + - Greengale 23 + - Bento 24 + 5. Render document content (markdown) in a reading view 25 + 26 + ## Feature: Subscribe to Publications 27 + 28 + - Authenticated users can create `site.standard.graph.subscription` records 29 + - Track subscriptions in sidebar alongside feed list 30 + 31 + ## Integration Points 32 + 33 + - AT Explorer: when browsing a repo, highlight standard.site collections with a distinct icon 34 + - Profile view: show "Publications" tab if the user has standard.site records 35 + - Search: index document text alongside posts for FTS/semantic search 36 + 37 + ## UX Polish 38 + 39 + - Publication cards: `Motion` scale-up on hover, spring easing 40 + - Document list: staggered `Motion` fade-in 41 + - Reading view: `Presence` slide-in from right (like turning a page) 42 + - Subscribe/unsubscribe: `Motion` pop on the icon toggle 43 + - Markdown content: smooth typography with comfortable reading width
+66
docs/specs/timeline.md
··· 1 + # Timeline & Social Features 2 + 3 + ## Timeline View 4 + 5 + Inspired by Aeronaut's timeline continuity — new posts prepend without losing scroll position. 6 + 7 + ### XRPC Endpoints (via jacquard) 8 + 9 + | Action | Lexicon | 10 + | ------------------ | --------------------------------------------------------- | 11 + | Following timeline | `app.bsky.feed.getTimeline` | 12 + | Custom feed | `app.bsky.feed.getFeed` | 13 + | Author feed | `app.bsky.feed.getAuthorFeed` | 14 + | Post thread | `app.bsky.feed.getPostThread` | 15 + | Like a post | `app.bsky.feed.like` (create record) | 16 + | Repost | `app.bsky.feed.repost` (create record) | 17 + | Create post | `com.atproto.repo.createRecord` with `app.bsky.feed.post` | 18 + | Get likes list | `app.bsky.feed.getActorLikes` | 19 + | Get profile | `app.bsky.actor.getProfile` | 20 + | Follow/unfollow | `app.bsky.graph.follow` (create/delete record) | 21 + | Mute/block | `app.bsky.graph.muteActor` / `app.bsky.graph.block` | 22 + 23 + ### Feed Preferences 24 + 25 + - Toggle reposts, replies, quote-posts per feed (like Aeronaut) 26 + - Store preferences per account in SQLite 27 + 28 + ## Post Composer 29 + 30 + - Rich text via `jacquard::richtext` — auto-detect mentions, links, hashtags 31 + - Image/media upload via `com.atproto.repo.uploadBlob` 32 + - Reply threading with parent/root refs 33 + - Quote post embed 34 + 35 + ## Notifications 36 + 37 + - `app.bsky.notification.listNotifications` — poll or use Jetstream 38 + - Separate tabs: Mentions vs Activity (Aeronaut pattern) 39 + - System notifications via Tauri 40 + 41 + ## Keyboard Shortcuts 42 + 43 + | Key | Action | 44 + | ------------- | ------------------------ | 45 + | `n` | New post (open composer) | 46 + | `j` / `k` | Next / previous post | 47 + | `l` | Like focused post | 48 + | `r` | Reply to focused post | 49 + | `t` | Repost focused post | 50 + | `o` / `Enter` | Open thread | 51 + | `1`–`9` | Switch between feeds | 52 + 53 + ## UX Polish 54 + 55 + - New posts slide in from top via `Motion` with spring easing; scroll position preserved 56 + - Like/repost actions: `Motion` scale pop on the icon (1.0 → 1.3 → 1.0) 57 + - Post card: subtle `Motion` fade-in on viewport enter during infinite scroll 58 + - Composer: `Presence` slide-up animation on open, slide-down on dismiss 59 + - Feed switcher: `Presence` crossfade between feed content on tab change 60 + - Skeleton screens while feeds load; error toast with retry button on network failure 61 + - Feed preferences stored per account in SQLite 62 + 63 + ## Direct Messages 64 + 65 + - `chat.bsky.convo.*` lexicons for DM support 66 + - Deferred to post-MVP unless trivial to add
+17
docs/tasks/01-backend-setup.md
··· 1 + # Task 01: Rust Backend Setup 2 + 3 + Spec: [mvp.md](../specs/mvp.md) 4 + 5 + ## Steps 6 + 7 + - [ ] Add Cargo dependencies: `jacquard`, `rusqlite` (bundled), `sqlite-vec`, `fastembed`, `tokio` 8 + - [ ] Add Tauri plugins: `tauri-plugin-deep-link`, `tauri-plugin-notification`, `tauri-plugin-updater` 9 + - [ ] Add frontend deps: `solid-motionone` (animation), install via npm 10 + - [ ] Create `src-tauri/src/db.rs` — initialize SQLite, run migrations, load `sqlite-vec` extension 11 + - [ ] Create migration system: `accounts`, `posts`, `posts_fts`, `posts_vec` tables 12 + - [ ] Create `src-tauri/src/state.rs` — `AppState` struct holding DB pool, active session, account list 13 + - [ ] Register `AppState` as Tauri managed state 14 + - [ ] Create Tauri command scaffold with error handling pattern (`Result<T, String>` or custom error type) 15 + - [ ] Set up dark/light theme: CSS custom properties, OS preference detection via `prefers-color-scheme` 16 + - [ ] Create global error toast component using `Presence` for enter/exit animations 17 + - [ ] Verify build compiles on macOS with `cargo tauri dev`
+23
docs/tasks/02-auth.md
··· 1 + # Task 02: Auth & Accounts 2 + 3 + Spec: [auth.md](../specs/auth.md) 4 + 5 + ## Steps 6 + 7 + - [ ] Implement `PersistentAuthStore` backed by SQLite (impl `jacquard_oauth::authstore` trait) 8 + - [ ] Create Tauri command `login(handle: String)`: 9 + - Resolve handle → authorization server 10 + - Build `AtprotoClientMetadata` for Lazurite 11 + - Start loopback OAuth via `LoopbackConfig` 12 + - Store session tokens, insert into `accounts` table 13 + - Return account info to frontend 14 + - [ ] Create Tauri command `logout(did: String)` — revoke tokens, remove from DB 15 + - [ ] Create Tauri command `switch_account(did: String)` — swap active `OAuthSession` in state 16 + - [ ] Create Tauri command `list_accounts()` → `Vec<Account>` 17 + - [ ] On app launch: restore sessions from DB, auto-refresh tokens for active account 18 + - [ ] Register `at://` scheme via deep-link plugin in `tauri.conf.json` 19 + - [ ] Handle deep-link events: parse `at://` URI, emit Tauri event to frontend for navigation 20 + - [ ] **Frontend**: login form with `Motion` spring shake on invalid handle 21 + - [ ] **Frontend**: account switcher dropdown in sidebar with `Presence` avatar enter/exit 22 + - [ ] **Frontend**: skeleton shimmer on profile card during session restore 23 + - [ ] **Frontend**: inline re-auth prompt with pulse animation on session expiry
+23
docs/tasks/03-timeline.md
··· 1 + # Task 03: Timeline & Feeds 2 + 3 + Spec: [timeline.md](../specs/timeline.md) 4 + 5 + ## Steps 6 + 7 + - [ ] Create `src-tauri/src/feed.rs` — Tauri commands for feed operations 8 + - [ ] `get_timeline(cursor: Option<String>, limit: u32)` — calls `app.bsky.feed.getTimeline` via jacquard Agent 9 + - [ ] `get_feed(uri: String, cursor: Option<String>)` — custom/list feeds 10 + - [ ] `get_post_thread(uri: String)` — thread view 11 + - [ ] `create_post(text: String, reply_to: Option<ReplyRef>, embed: Option<Embed>)` — with richtext facet detection via `jacquard::richtext` 12 + - [ ] `like_post(uri: String, cid: String)` / `unlike_post(uri: String)` 13 + - [ ] `repost(uri: String, cid: String)` / `unrepost(uri: String)` 14 + - [ ] `get_author_feed(did: String, cursor: Option<String>)` 15 + - [ ] **Frontend**: timeline component with infinite scroll, scroll-position preservation 16 + - [ ] **Frontend**: post card component (text, embeds, actions bar) — `Motion` fade-in on viewport enter 17 + - [ ] **Frontend**: like/repost icon `Motion` scale pop animation (1.0 → 1.3 → 1.0) 18 + - [ ] **Frontend**: post composer with `Presence` slide-up/down, mention/hashtag autocomplete 19 + - [ ] **Frontend**: thread view with nested replies 20 + - [ ] **Frontend**: feed selector (Following, custom feeds, lists) with `Presence` crossfade on switch 21 + - [ ] **Frontend**: feed preferences toggle (hide reposts/replies/quotes), persisted per account 22 + - [ ] **Frontend**: skeleton screens while feeds load 23 + - [ ] **Frontend**: keyboard shortcuts — `n` post, `j/k` navigate, `l` like, `r` reply, `t` repost, `o` open, `1-9` feeds
+16
docs/tasks/04-notifications.md
··· 1 + # Task 04: Notifications 2 + 3 + Spec: [timeline.md](../specs/timeline.md) 4 + 5 + ## Steps 6 + 7 + - [ ] Create `src-tauri/src/notifications.rs` 8 + - [ ] `list_notifications(cursor: Option<String>)` — `app.bsky.notification.listNotifications` 9 + - [ ] `update_seen()` — `app.bsky.notification.updateSeen` 10 + - [ ] `get_unread_count()` — `app.bsky.notification.getUnreadCount` 11 + - [ ] Background polling: spawn async task on login, poll every 30s, emit Tauri event on new notifications 12 + - [ ] **Frontend**: notifications panel with two tabs — Mentions / Activity (Aeronaut pattern) 13 + - [ ] **Frontend**: unread badge on sidebar notification icon with `Motion` scale-in pop 14 + - [ ] **Frontend**: new notification items `Motion` slide-in from top 15 + - [ ] **Frontend**: tab switch `Presence` crossfade between Mentions/Activity 16 + - [ ] System notifications via `tauri-plugin-notification` for mentions when app is in background
+24
docs/tasks/05-explorer.md
··· 1 + # Task 05: AT Explorer 2 + 3 + Spec: [explorer.md](../specs/explorer.md) 4 + 5 + ## Steps 6 + 7 + - [ ] Create `src-tauri/src/explorer.rs` — Tauri commands for AT data browsing 8 + - [ ] `resolve_input(input: String)` — detect if input is at:// URI, handle, DID, or PDS URL; resolve accordingly 9 + - [ ] `describe_server(pds_url: String)` — `com.atproto.server.describeServer` 10 + - [ ] `describe_repo(did: String)` — `com.atproto.repo.describeRepo` 11 + - [ ] `list_records(did: String, collection: String, cursor: Option<String>)` — `com.atproto.repo.listRecords` 12 + - [ ] `get_record(did: String, collection: String, rkey: String)` — `com.atproto.repo.getRecord` 13 + - [ ] `export_repo_car(did: String)` — `com.atproto.sync.getRepo`, save to file 14 + - [ ] `query_labels(uri: String)` — `com.atproto.label.queryLabels` 15 + - [ ] Wire deep-link handler: `at://` URI → parse → call `resolve_input` → emit navigation event 16 + - [ ] **Frontend**: explorer URL bar with input parsing, `Cmd+L` to focus 17 + - [ ] **Frontend**: PDS view — server info + hosted account list, skeleton loading 18 + - [ ] **Frontend**: repo view — collection list with record counts 19 + - [ ] **Frontend**: collection view — paginated record list 20 + - [ ] **Frontend**: record view — syntax-highlighted JSON with collapsible sections, type-specific rendering 21 + - [ ] **Frontend**: breadcrumb navigation bar with `Motion` width animation on segment changes 22 + - [ ] **Frontend**: `Presence` crossfade transitions between explorer view levels 23 + - [ ] **Frontend**: keyboard shortcuts — `Backspace` up a level, `Cmd+[/]` back/forward 24 + - [ ] **Optional**: Jetstream live-tail view with `Motion` slide-in for new records
+29
docs/tasks/06-search.md
··· 1 + # Task 06: Search & Embeddings 2 + 3 + Spec: [search.md](../specs/search.md) 4 + 5 + ## Steps 6 + 7 + - [ ] Create `src-tauri/src/search.rs` 8 + - [ ] Implement `sync_posts(did: String, source: "like"|"bookmark")`: 9 + - Paginate `app.bsky.feed.getActorLikes` (or bookmarks) 10 + - Upsert into `posts` table 11 + - Insert text into `posts_fts` 12 + - Track sync cursor in `sync_state` table 13 + - [ ] Implement `embed_pending_posts()`: 14 + - Query posts without embeddings 15 + - Batch through `fastembed` TextEmbedding model (`nomic-embed-text-v1.5`) 16 + - Insert into `posts_vec` via `zerocopy::AsBytes` 17 + - [ ] Implement `search_posts(query, mode, limit)`: 18 + - `keyword`: FTS5 MATCH query 19 + - `semantic`: embed query string → vec similarity search 20 + - `hybrid`: run both, merge via reciprocal rank fusion 21 + - [ ] `get_sync_status(did)` → last sync time, post counts 22 + - [ ] Model management: download `nomic-embed-text-v1.5` ONNX on first use to `app_data_dir/models/` 23 + - [ ] Background sync: trigger after login, then every 15 min 24 + - [ ] **Frontend**: search bar (`/` to focus) with mode selector, `Motion` sliding indicator underline 25 + - [ ] **Frontend**: search results with staggered `Motion` fade-in, highlighted keyword matches 26 + - [ ] **Frontend**: sync status indicator with animated progress bar, `Presence` fade-out on complete 27 + - [ ] **Frontend**: model download progress bar (percentage + ETA) on first launch 28 + - [ ] **Frontend**: empty state illustration when no posts synced yet 29 + - [ ] **Frontend**: `Tab` cycles search mode, `Escape` clears
+19
docs/tasks/07-standard-site.md
··· 1 + # Task 07: Standard.site Integration 2 + 3 + Spec: [standard-site.md](../specs/standard-site.md) 4 + 5 + ## Steps 6 + 7 + - [ ] Create `src-tauri/src/publications.rs` 8 + - [ ] `get_publications(did: String)` — list `site.standard.publication` records from repo 9 + - [ ] `get_documents(did: String, cursor: Option<String>)` — list `site.standard.document` records 10 + - [ ] `get_document(did: String, rkey: String)` — fetch single document content 11 + - [ ] `subscribe_publication(did: String, rkey: String)` — create `site.standard.graph.subscription` record 12 + - [ ] `unsubscribe_publication(uri: String)` — delete subscription record 13 + - [ ] `list_subscriptions()` — list user's publication subscriptions 14 + - [ ] **Frontend**: publication card with `Motion` scale-up on hover 15 + - [ ] **Frontend**: document list with staggered `Motion` fade-in 16 + - [ ] **Frontend**: markdown reader view with `Presence` slide-in from right 17 + - [ ] **Frontend**: subscribe/unsubscribe `Motion` pop on icon toggle 18 + - [ ] **Frontend**: "Publications" tab on profile views (when records exist) 19 + - [ ] **Search integration**: index document text in `posts_fts` and `posts_vec`
+9
docs/tasks/08-release.md
··· 1 + # Task 08: Release 2 + 3 + ## Steps 4 + 5 + - [ ] App icon and branding 6 + - [ ] Auto-update via `tauri-plugin-updater` pointing to GitHub Releases 7 + - [ ] macOS code signing and notarization 8 + - [ ] DMG packaging via `tauri build` 9 + - [ ] Smoke test: fresh install flow, OAuth login, timeline load, search sync
+23
docs/tasks/mvp.md
··· 1 + # Lazurite Desktop — MVP Task Breakdown 2 + 3 + Tasks are grouped by module. Each references the relevant spec. Polish (keyboard shortcuts, animations via `solid-motionone`, loading states, accessibility) is built into each task — not deferred. 4 + 5 + ## Phase 1: Foundation 6 + 7 + - [Rust Backend Setup](./01-backend-setup.md) — Cargo deps, SQLite, Tauri commands scaffold, theme, error toast, `solid-motionone` 8 + - [Auth & Accounts](./02-auth.md) — OAuth loopback, multi-account, session persistence, account switcher animations 9 + 10 + ## Phase 2: Core Social 11 + 12 + - [Timeline & Feeds](./03-timeline.md) — Feed views, post rendering, composer, keyboard shortcuts, scroll animations 13 + - [Notifications](./04-notifications.md) �� Mentions, activity, system notifications, badge animations 14 + 15 + ## Phase 3: Power Features 16 + 17 + - [AT Explorer](./05-explorer.md) — pds.ls-style data browser, at:// deep links, view transitions, keyboard nav 18 + - [Search & Embeddings](./06-search.md) — FTS5, fastembed, sqlite-vec, sync pipeline, result animations 19 + 20 + ## Phase 4: Long-Form & Release 21 + 22 + - [Standard.site](./07-standard-site.md) — Publication/document views, subscriptions, reading view transitions 23 + - [Release](./08-release.md) — macOS code signing, notarization, DMG packaging, auto-update
+5 -1
eslint.config.js
··· 24 24 }, 25 25 { files: ["scripts/**/*.{js,mjs,cjs}", "vite.config.ts"], languageOptions: { globals: globals.node } }, 26 26 { files: ["**/*.tsx"], plugins: { react }, rules: { "react/jsx-max-depth": ["error", { max: 4 }] } }, 27 - { files: ["**/*.tsx"], ...solidConfig, rules: { "solid/no-innerhtml": "off" } }, 27 + { 28 + files: ["**/*.tsx"], 29 + ...solidConfig, 30 + rules: { ...solidConfig.rules, "solid/no-innerhtml": "off", "solid/prefer-for": "error" }, 31 + }, 28 32 { 29 33 rules: { 30 34 "unicorn/catch-error-name": "off",
+3 -9
src/App.tsx
··· 1 + import { invoke } from "@tauri-apps/api/core"; 1 2 import { createSignal } from "solid-js"; 2 3 import logo from "./assets/logo.svg"; 3 - import { invoke } from "@tauri-apps/api/core"; 4 4 import "./App.css"; 5 5 6 6 function App() { ··· 15 15 return ( 16 16 <main class="container"> 17 17 <h1>Welcome to Tauri + Solid</h1> 18 - 19 18 <div class="row"> 20 19 <a href="https://vite.dev" target="_blank"> 21 20 <img src="/vite.svg" class="logo vite" alt="Vite logo" /> ··· 34 33 onSubmit={(e) => { 35 34 e.preventDefault(); 36 35 greet(); 37 - }} 38 - > 39 - <input 40 - id="greet-input" 41 - onChange={(e) => setName(e.currentTarget.value)} 42 - placeholder="Enter a name..." 43 - /> 36 + }}> 37 + <input id="greet-input" onChange={(e) => setName(e.currentTarget.value)} placeholder="Enter a name..." /> 44 38 <button type="submit">Greet</button> 45 39 </form> 46 40 <p>{greetMsg()}</p>