1 line
8.8 KiB
JSON
1 line
8.8 KiB
JSON
{"repo": "bsquang/naotab", "files": {"CLAUDE.md": {"ed23f22c804b4e00a2aa21149ff95484fe582e1a": {"t": 1785674436.627878, "n": 8110, "h": "449bc07c2a84305a", "c": "# naoTab — Chrome Extension\n\n## Project overview\n\nChrome Extension (Manifest V3) that turns browser tabs into a **personal knowledge base**. No server, no backend — everything runs inside the extension, data stored in `chrome.storage.local`.\n\nUI language: **English**. Codebase comments: mixed EN/VI.\n\n---\n\n## File structure\n\n```\nnaoTab/\n├── manifest.json # Manifest V3, permissions: tabs + storage + unlimitedStorage + scripting\n├── popup.html/css/js # Popup — view tabs, save, export\n├── app.html/js # Knowledge Base full-page\n├── settings.html/js # AI provider config page\n├── build.sh # Build script → naotab-vX.Y.Z.zip\n├── core/\n│ ├── schema.js # ⚠️ Single source of truth for data structure — never remove fields\n│ ├── storage.js # Bookmarks CRUD + settings (reads only, no business logic)\n│ ├── ai.js # callAI() + suggestTags()\n│ └── export.js # exportJSON / importJSON / exportObsidian / bookmarkToObsidianMd\n│ └── sync/ # (future) drive.js, notion.js, etc.\n├── vendor/\n│ ├── d3.min.js # D3.js v7.9.0 — bundled locally (CSP compliance)\n│ └── jszip.min.js # JSZip 3.10.1 — bundled locally (CSP compliance)\n├── icons/ # icon16/48/128.png\n├── README.md # English README (links to VI version)\n├── README.vi.md # Vietnamese README (links to EN version)\n└── CLAUDE.md # This file\n```\n\n---\n\n## Architecture & Data flow\n\n```\npopup.js ──────────────────────┐\nsettings.js ────────────────────┤──► storage.js ──► chrome.storage.local\napp.js ────────────────────────┘ │\n └──► External AI API (optional)\n```\n\n### Data schema — bookmark object\n\n```json\n{\n \"id\": \"1712345678901\",\n \"url\": \"https://...\",\n \"title\": \"Page title\",\n \"reason\": \"Why user saved this\",\n \"summary\": \"AI-generated or manual summary\",\n \"tags\": [\"rust\", \"async\", \"performance\"],\n \"favIconUrl\": \"https://...\",\n \"pageMeta\": {\n \"description\": \"og:description or meta description\",\n \"ogTitle\": \"og:title\",\n \"ogType\": \"article\",\n \"keywords\": \"meta keywords\",\n \"author\": \"meta author\",\n \"siteName\": \"og:site_name\",\n \"ogImage\": \"og:image URL\",\n \"lang\": \"html lang attribute\",\n \"canonical\": \"canonical URL\"\n },\n \"savedAt\": \"2024-01-01T00:00:00.000Z\",\n \"updatedAt\": \"2024-01-01T00:00:00.000Z\"\n}\n```\n\nNote: `status` field removed from UI (was: unread/reading/done/revisit). `pageMeta` is `null` if meta read failed or tab was a chrome:// page.\n\n### Data schema — settings object\n\n```json\n{\n \"aiEnabled\": false,\n \"aiBaseUrl\": \"https://api.openai.com/v1\",\n \"aiApiKey\": \"sk-...\",\n \"aiModel\": \"gpt-4o-mini\",\n \"featTags\": true,\n \"featSummary\": true\n}\n```\n\n---\n\n## Key files and responsibilities\n\n### `core/schema.js`\n⚠️ **Never remove or rename existing fields.** Only ADD new fields with a default value.\n\n- `SCHEMA_VERSION` — current version integer, increment when adding fields\n- `BOOKMARK_DEFAULTS` — canonical bookmark shape with all fields and defaults\n- `SETTINGS_DEFAULTS` — canonical settings shape\n- `migrateBookmark(raw)` — heals old bookmark objects: fills missing fields, drops removed ones\n- `createBookmark(fields)` — builds a new bookmark object with all required fields\n\n### `core/storage.js`\nCRUD only. Loaded into popup, app, settings pages.\n\n- `getBookmarks()` — read all bookmarks, runs `migrateBookmark()` on each (safe for old data)\n- `saveBookmark(fields)` — save new, dedup by URL, calls `createBookmark()`\n- `updateBookmark(id, changes)` — partial update\n- `deleteBookmark(id)` — delete by id\n- `getSettings()` / `saveSettings(settings)` — read/write settings\n\n### `core/ai.js`\n- `callAI(title, url, pageMetaText)` — call AI API, returns `{tags, summary}` or throws\n- `suggestTags(title, url)` — offline keyword + domain matching, returns tag array\n\n### `core/export.js`\n- `exportJSON()` / `importJSON(jsonString)` — full backup/restore\n- `bookmarkToObsidianMd(bookmark)` / `exportObsidian()` — Obsidian .md export\n\n### `popup.js`\n- `loadTabs()` — queries all tabs + bookmarks, marks saved URLs\n- `getPageMeta(tabId)` — uses `chrome.scripting.executeScript` to read SEO meta tags; returns structured object with `_aiText` field for AI prompt\n- `openSaveModal(tab)` — async, reads meta in background, shows/hides AI row\n- Window save: calls `getPageMeta()` for each tab, shows `⏳ N/total` progress, saves `pageMeta`\n- Single tab save: passes `pageMeta` to `saveBookmark()`\n- **\"💾 Tab this\"** button — saves active tab directly from toolbar\n\n### `app.js`\nState: `allBookmarks`, `activeTag`, `excludedTags` (Set), `searchQuery`, `currentView` (default: `'graph'`), `editingId`, `panelId`\n\nKey features:\n- **Graph view** (default): D3 force simulation, single-click → node panel + highlight connected nodes, double-click → open URL, click background → reset. Node color: blue (has summary) vs grey (no summary)\n- **List view**: card per bookmark, click card → node panel (links/buttons still work normally)\n- **Node panel**: slide-in right panel with full details, editable fields, AI Suggest button (shown only when AI enabled), Connected nodes section (only within current filter), save/delete/open-url actions\n- **Sidebar**: split into Tags (top, scrollable) + Node list (bottom, realtime). Node list updates with filter/search; click item → open panel; hover item → tooltip with title + summary\n- **Graph tooltip**: shows title, URL, summary (blue border), reason, tags on hover\n- **AI Batch**: `✨ AI All` button in topbar — processes only visible nodes without a summary, shows confirm with counts, progress bar + notify in bottom-right, nodes update color live\n- **Exclude tags**: tag pills in sidebar have `✕` button on hover → excluded tags shown as strikethrough red, not used for graph edges\n- **Delete all**: button in topbar with confirmation\n- **Refresh**: 🔄 button in topbar\n- **Obsidian export**: JSZip → .zip of .md files with YAML frontmatter\n\n### `settings.js`\n- PRESETS: openai, claude, ollama, groq, openrouter, custom\n- Detects active preset by matching saved URL\n- Test connection: detects Anthropic vs OpenRouter vs standard; adds `HTTP-Referer` + `X-Title` headers for OpenRouter\n- Save settings to `chrome.storage.local`\n\n---\n\n## AI Integration\n\n`callAI()` in `storage.js` supports 2 formats:\n\n**OpenAI-compatible** (OpenAI, Groq, Ollama, OpenRouter, etc.):\n- Endpoint: `{baseUrl}/chat/completions`\n- Header: `Authorization: Bearer {apiKey}`\n- OpenRouter also needs: `HTTP-Referer: https://github.com/bsquang/naotab` + `X-Title: naoTab`\n\n**Anthropic native**:\n- Detect: `baseUrl.includes('anthropic.com')`\n- Endpoint: `{baseUrl}/messages`\n- Header: `x-api-key: {apiKey}` + `anthropic-version: 2023-06-01`\n\n**Token efficiency**: AI receives page meta tags (~75 tokens) instead of body text (~750 tokens) — 10× cheaper. `_aiText` field is a preformatted string built from `pageMeta` fields.\n\n---\n\n## Permissions\n\n| Permission | Reason |\n|---|---|\n| `tabs` | Read title, URL, favIconUrl of all tabs |\n| `storage` | Save bookmarks and settings to chrome.storage.local |\n| `unlimitedStorage` | Remove 10MB default cap |\n| `scripting` | Execute script in tabs to read meta tags |\n| `host_permissions: <all_urls>` | Allow meta reading on any tab |\n\n---\n\n## Conventions when working with Claude\n\n- Claude edits files directly in this folder\n- User reloads extension via 🔄 button in popup\n- `storage.js` is shared — when adding functions, update popup.js and/or app.js as needed\n- No ES modules (`import/export`) — Chrome Extension uses classic script loading\n- No inline scripts in HTML — CSP requires all JS in external `.js` files\n- No CDN scripts — bundle all libraries locally for CSP compliance\n- Commit only when user explicitly asks\n- All UI text in **English**\n\n## Roadmap\n\n- [ ] Google Drive sync (`core/sync/drive.js` — branch: feature/google-drive-sync)\n- [ ] Semantic / AI-powered search\n- [ ] AI Cluster Summary (summarize all visible nodes as a group)\n- [ ] Dark mode\n- [ ] Duplicate tab detector\n- [ ] AI-powered bookmark grouping\n- [ ] Browser history integration\n"}}}} |