···11# Multicolumn Views
2233-TweetDeck-style layout allowing users to view multiple feeds, AT Explorer panels, and social diagnostics panels side by side. Each column is an independent, scrollable pane with its own state.
33+TweetDeck-style layout allowing users to view multiple feeds, AT Explorer panels, social diagnostics panels, DMs, search views, and profiles side by side. Each column is an independent, scrollable pane with its own state.
4455## Layout Model
66···6262- Tab navigation within the column
6363- Compact card layout adapted to column width
64646565+### Messages Column
6666+6767+Displays the authenticated user's DM inbox and active conversation.
6868+6969+- Reuses the existing messages panel
7070+- Sensitive content is blurred by default until the column is hovered or focused
7171+- Width should remain user-adjustable because compact layouts can still be useful for list-first triage
7272+7373+### Search Column
7474+7575+Displays a saved search query inside the deck.
7676+7777+- Reuses the search panel in an embedded mode
7878+- Persists the initial query and search mode in column config
7979+- Supports network and local search modes
8080+8181+### Profile Column
8282+8383+Displays a profile view for a selected actor.
8484+8585+- Reuses the existing profile panel
8686+- Column picker should prefer actor typeahead to reduce handle/DID entry errors
8787+- Persist actor selection in column config so the column can be restored on launch
8888+6589## Column Management
66906791### Adding Columns
···7195- **Feed picker**: pinned feeds, saved feeds, list feeds
7296- **Explorer picker**: input field accepting at:// URI, handle, DID, or PDS URL
7397- **Diagnostics picker**: input field accepting handle or DID
9898+- **Messages picker**: opens the authenticated user's DM inbox
9999+- **Search picker**: query + search mode
100100+- **Profile picker**: actor lookup with typeahead
7410175102New columns append to the right by default. Optional position insertion via drag during add.
76103···82109CREATE TABLE columns (
83110 id TEXT PRIMARY KEY,
84111 account_did TEXT NOT NULL,
8585- kind TEXT NOT NULL, -- 'feed' | 'explorer' | 'diagnostics'
8686- config TEXT NOT NULL, -- JSON: feed → { feed_uri, feed_type }, explorer → { target_uri }, diagnostics → { did }
112112+ kind TEXT NOT NULL, -- 'feed' | 'explorer' | 'diagnostics' | 'messages' | 'search' | 'profile'
113113+ config TEXT NOT NULL, -- JSON: feed → { feed_uri, feed_type }, explorer → { target_uri }, diagnostics → { did }, messages → {}, search → { query, mode }, profile → { actor, handle?, did?, display_name? }
87114 position INTEGER NOT NULL,
88115 width TEXT NOT NULL DEFAULT 'standard',
89116 created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
+21-3
docs/tasks/08-multicolumn.md
···1111### Backend - `src-tauri/src/columns.rs` + `src-tauri/src/commands/columns.rs`
12121313- [x] SQLite migration: `columns` table (`id TEXT PRIMARY KEY, account_did TEXT, kind TEXT, config TEXT, position INTEGER, width TEXT, created_at TEXT`)
1414- - `kind`: `feed` | `explorer` | `diagnostics` - determines the column type
1515- - `config`: JSON blob - for feeds: `{ feed_uri, feed_type }`, for explorer: `{ target_uri }`, for diagnostics: `{ did }`
1414+ - `kind`: `feed` | `explorer` | `diagnostics` | `messages` | `search` | `profile` - determines the column type
1515+ - `config`: JSON blob - for feeds: `{ feed_uri, feed_type }`, for explorer: `{ target_uri }`, for diagnostics: `{ did }`, for messages: `{}`, for search: `{ query, mode }`, for profile: `{ actor, handle?, did?, displayName? }`
1616 - `width`: `narrow` | `standard` | `wide`
1717- [x] `get_columns(account_did: String)` - return ordered column list for the active account
1818- [x] `add_column(account_did: String, kind: String, config: String, position: Option<u32>)` - insert at position or append
···5151- [ ] Tab navigation within column for lists/labels/blocks/starter packs/backlinks
5252- [ ] Compact card layout adapted to column width
53535454+#### Messages Column
5555+5656+- [x] Reuse the existing messages panel inside deck columns
5757+- [x] Blur DM content until hovered or focused
5858+5959+#### Search Column
6060+6161+- [x] Reuse the existing search panel inside deck columns
6262+- [x] Persist search query + mode in column config
6363+6464+#### Profile Column
6565+6666+- [x] Reuse the existing profile panel inside deck columns
6767+- [x] Add profile column creation via actor typeahead
6868+5469### Frontend - Column Management
55705671- [x] "Add column" button (`i-ri-add-line`) opens a picker panel:
5772 - Feed picker: lists pinned feeds, saved feeds, list feeds
5873 - Explorer picker: input field for at:// URI, handle, DID, or PDS URL
5974 - Diagnostics picker: input field for handle or DID
7575+ - Messages picker: opens DM inbox
7676+ - Search picker: accepts query + mode
7777+ - Profile picker: typeahead-first actor selection
6078- [ ] Right-click column header for context menu (resize, duplicate, close)
6179- [x] Keyboard shortcuts: `Ctrl+Shift+N` add column, `Ctrl+Shift+W` close focused column
6280- [x] Persist column layout to SQLite per account - restore on app launch
···65836684- [ ] Column templates / saved layouts (e.g., "Research", "Timeline + Notifications")
6785- [ ] Notification column type
6868-- [ ] Search results column type
8686+- [x] Search results column type
6987- [ ] Column-level auto-refresh interval override
7088- [ ] Shared scroll sync between related columns
+2-2
src-tauri/src/columns.rs
···185185186186fn validate_kind(kind: &str) -> Result<()> {
187187 match kind {
188188- "feed" | "explorer" | "diagnostics" => Ok(()),
188188+ "feed" | "explorer" | "diagnostics" | "messages" | "search" | "profile" => Ok(()),
189189 _ => Err(AppError::validation(format!(
190190- "invalid column kind '{kind}': must be 'feed', 'explorer', or 'diagnostics'"
190190+ "invalid column kind '{kind}': must be 'feed', 'explorer', 'diagnostics', 'messages', 'search', or 'profile'"
191191 ))),
192192 }
193193}
···11+CREATE TABLE columns_next (
22+ id TEXT PRIMARY KEY,
33+ account_did TEXT NOT NULL,
44+ kind TEXT NOT NULL CHECK(kind IN ('feed', 'explorer', 'diagnostics', 'messages', 'search', 'profile')),
55+ config TEXT NOT NULL,
66+ position INTEGER NOT NULL,
77+ width TEXT NOT NULL DEFAULT 'standard' CHECK(width IN ('narrow', 'standard', 'wide')),
88+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
99+);
1010+1111+INSERT INTO columns_next (id, account_did, kind, config, position, width, created_at)
1212+SELECT id, account_did, kind, config, position, width, created_at
1313+FROM columns;
1414+1515+DROP TABLE columns;
1616+1717+ALTER TABLE columns_next RENAME TO columns;
1818+1919+CREATE INDEX IF NOT EXISTS columns_account_did ON columns(account_did, position);