Archipelago v1.7.129-alpha
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
# Plan: Overhaul Content Detection + Add Apps Tab
|
||||
|
||||
## Context
|
||||
|
||||
The content surfacing system misses many common AI response patterns. Example: AI responds about Nostr (mentioning damus.io, primal.net, snort.social) but the Nostr tab never surfaces. Query/response classifiers use narrow regexes that miss natural language variations. There's no "topic detection" layer, no app detection, and bare domains in AI text aren't extracted as websites.
|
||||
|
||||
**Goals:**
|
||||
1. Fix content detection to handle how AIs actually respond
|
||||
2. Add Nostr tab surfacing (currently only via `/nostr` command)
|
||||
3. Add Apps tab with curated Nostr + Bitcoin ecosystem apps (local DB + AI extraction fallback)
|
||||
4. Extract bare domains from AI text (e.g. "check out damus.io")
|
||||
|
||||
---
|
||||
|
||||
## Part 1: Expand Query & Response Classifiers
|
||||
|
||||
**File:** `packages/app/src/composables/contentFiltering.ts`
|
||||
|
||||
### 1A. Add Nostr classifiers (new functions)
|
||||
|
||||
- `isNostrQuery(q)` — matches: nostr, npub, nip-\d, damus, primal, snort, amethyst, coracle, zap, relay, note1, nevent, nprofile, fiatjaf, nostrich, "decentralized social"
|
||||
- `isNostrLikeResponse(text)` — requires literal "nostr" OR 2+ Nostr-specific signals (npub, nip-, client names, relay+wss, zap+lightning)
|
||||
|
||||
### 1B. Add App classifiers (new functions)
|
||||
|
||||
- `isAppQuery(q)` — matches: app, client, wallet, tool, software, download, install, "what app", "best app for", "recommend.*app"
|
||||
- `isAppLikeResponse(text)` — matches: "you can use", "popular clients include", "I'd recommend", "available on", "download from"
|
||||
|
||||
### 1C. Expand existing classifiers with broader patterns
|
||||
|
||||
| Classifier | Add these patterns |
|
||||
|---|---|
|
||||
| `isNewsQuery` | "what happened today", "any updates on", "trending", "catch me up", "brief me", "current events" |
|
||||
| `isMusicQuery` | "genre", "spotify", "bandcamp", "grammys", "billboard", "mixtape", "discography", "banger", "favorite jam" |
|
||||
| `isBookQuery` | "what should I read", "favorite reads", "reading list", "book club", "memoir", "audiobook", "goodreads", "worth reading" |
|
||||
| `isTVQuery` | "what's good on netflix", "anything to binge", "hbo", "disney+", "apple tv", "amazon prime", "docuseries", "limited series" |
|
||||
| `isPlaceQuery` | "hungry", "food near me", "best brunch spot", "happy hour", "speakeasy", "rooftop bar", "food truck" |
|
||||
| `isWebsitesQuery` | "point me to", "link me", "any good sites", "tools for", "platforms for" |
|
||||
| `isWebsitesLikeResponse` | "here are some resources", "I'd recommend checking", "you can visit", "useful resources" |
|
||||
| `isNewsLikeResponse` | "I can't access the web but", "having trouble reaching", "unable to browse but" |
|
||||
|
||||
### 1D. Update `preferredFirstTab()` — add nostr + app checks
|
||||
|
||||
### 1E. Update `filterTabsByContext()` — add `hasNostr` and `hasApps` params, integrate into tab ordering
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Bare Domain Extraction
|
||||
|
||||
**File:** `packages/app/src/composables/contentExtraction.ts`
|
||||
|
||||
Add `extractBareDomainLinks(text)`:
|
||||
- Detect plain-text domains like "damus.io", "primal.net" not inside markdown links or bold patterns
|
||||
- Skip positions covered by existing extractors (markdown links, bold-domain, full URLs)
|
||||
- Require known TLDs (.com, .org, .io, .net, .social, .app, etc.)
|
||||
- Block file extensions (.js, .ts, .vue, .json, .css)
|
||||
- Use existing `normUrl()` for dedup
|
||||
|
||||
---
|
||||
|
||||
## Part 3: Apps Tab — Curated Database + AI Extraction
|
||||
|
||||
### 3A. Create app database
|
||||
|
||||
**New file:** `packages/app/src/data/apps.ts`
|
||||
|
||||
```ts
|
||||
interface AppEntry {
|
||||
id: string
|
||||
name: string
|
||||
description: string // One-liner
|
||||
longDescription: string // Why use this, how it works
|
||||
category: 'nostr-client' | 'lightning-wallet' | 'bitcoin-wallet' | 'privacy' | 'node' | 'dev-tool' | 'relay'
|
||||
platforms: ('ios' | 'android' | 'web' | 'desktop' | 'cli' | 'nodeos')[]
|
||||
url: string
|
||||
icon?: string
|
||||
keywords: string[] // For matching AI responses
|
||||
howTo?: string[] // Getting started steps
|
||||
relatedApps?: string[] // IDs of related apps
|
||||
}
|
||||
```
|
||||
|
||||
**Initial curated apps (~25-30):**
|
||||
- Nostr clients: Damus, Primal, Snort, Amethyst, Coracle, Iris, Nostrudel, nos.social
|
||||
- Lightning wallets: Phoenix, Mutiny, Breez, Zeus, Alby, Wallet of Satoshi
|
||||
- Bitcoin wallets: Sparrow, Blue Wallet, Nunchuk, Coldcard, Green
|
||||
- Privacy tools: Tor, SimpleX Chat, Signal, Mullvad VPN
|
||||
- Node software: Start9, Umbrel, RaspiBlitz, myNode
|
||||
- Dev tools: NDK, nostr-tools, Nak
|
||||
|
||||
### 3B. Add app extraction
|
||||
|
||||
**File:** `packages/app/src/composables/contentExtraction.ts`
|
||||
|
||||
Add `extractApps(text, userQuery)`:
|
||||
1. Match AI text against known app names/keywords from database
|
||||
2. If app query detected OR 2+ known apps mentioned → return matched apps
|
||||
3. For unknown apps, create basic entries from context (name + URL if bare domain found)
|
||||
|
||||
### 3C. Create UI components
|
||||
|
||||
**New files:**
|
||||
- `packages/app/src/components/content/AppsGrid.vue` — Grid of app cards (icon, name, category badge, one-liner)
|
||||
- `packages/app/src/components/content/AppDetail.vue` — Detail: icon, name, platforms, long description, how-to steps, link, related apps
|
||||
|
||||
Follow existing grid/detail patterns (e.g. `BookGrid.vue`/`BookDetail.vue`).
|
||||
|
||||
### 3D. Register in ContentPanel.vue
|
||||
|
||||
Add rendering for `activeTab === 'app'`, add `'app'` to `ContentTab` type.
|
||||
|
||||
---
|
||||
|
||||
## Part 4: Wire Everything Together
|
||||
|
||||
**File:** `packages/app/src/composables/useContentPanel.ts`
|
||||
|
||||
In `updatePanelFromText()`:
|
||||
- Call `extractBareDomainLinks(text)`, merge with website sources
|
||||
- Call `extractApps(text, userQuery)`
|
||||
- Compute `hasNostr = isNostrQuery(userQuery) || isNostrLikeResponse(text)`
|
||||
- Compute `hasApps = apps.length > 0`
|
||||
- Pass `hasNostr` and `hasApps` to `filterTabsByContext()`
|
||||
- Add `panelApps` ref, title logic for apps/nostr tabs
|
||||
|
||||
Same changes in `getContextualInlineContent()`.
|
||||
|
||||
Broaden magazine detection: add tech/protocol keywords, surface magazine for 3+ sections with no other structured content.
|
||||
|
||||
---
|
||||
|
||||
## Part 5: PromptIndex badges
|
||||
|
||||
**File:** `packages/app/src/components/chat/PromptIndex.vue`
|
||||
|
||||
Add 'Nostr' and 'Apps' badge detection.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. `contentFiltering.ts` — classifiers + filterTabsByContext signature
|
||||
2. `contentExtraction.ts` — `extractBareDomainLinks()` + `extractApps()`
|
||||
3. `data/apps.ts` — curated app database
|
||||
4. `useContentPanel.ts` — wire everything
|
||||
5. `AppsGrid.vue` + `AppDetail.vue` — UI components
|
||||
6. `ContentPanel.vue` — register tab + components
|
||||
7. `PromptIndex.vue` — badges
|
||||
8. Typecheck + manual test
|
||||
|
||||
## Verification
|
||||
|
||||
1. `pnpm typecheck` passes
|
||||
2. "tell me about Nostr" → Nostr + magazine tabs surface
|
||||
3. "best Nostr clients?" → Apps tab with Damus, Primal, Snort
|
||||
4. "recommend a bitcoin wallet" → Apps tab with Phoenix, Sparrow
|
||||
5. "what happened with BIP 110?" → Magazine tab (regression)
|
||||
6. "best movies of 2024" → Films tab (regression)
|
||||
7. Bare domains in AI text extracted as websites
|
||||
8. PromptIndex badges show Nostr/Apps
|
||||
Reference in New Issue
Block a user