feat: brain detail sheet — screenshot for links, editable notes, spelling fix

- Link detail: shows screenshot image (clickable to open URL), URL, summary, tags
- Note detail: click note text to edit, save/cancel buttons
- Notes: AI now fixes spelling/grammar instead of rewriting
- AI returns corrected_text field for notes, worker replaces raw_content
- Removed verbose meta grid (folder/confidence/status/saved)
- Folder shown as a pill badge in meta line

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-01 18:27:27 -05:00
parent 6694795726
commit 2c3f0d263b
3 changed files with 44 additions and 12 deletions

View File

@@ -401,12 +401,25 @@
</button>
</div>
<!-- Link: show screenshot + URL + tags -->
{#if selectedItem.type === 'link'}
{#if selectedItem.assets?.some(a => a.asset_type === 'screenshot')}
<a class="detail-screenshot" href={selectedItem.url} target="_blank" rel="noopener">
<img src="/api/brain/storage/{selectedItem.id}/screenshot/screenshot.png" alt="" />
</a>
{/if}
{#if selectedItem.url}
<a class="detail-url" href={selectedItem.url} target="_blank" rel="noopener">{selectedItem.url}</a>
{/if}
{#if selectedItem.summary}
<div class="detail-summary">{selectedItem.summary}</div>
{/if}
{/if}
<!-- Note: editable content -->
{#if selectedItem.type === 'note'}
<!-- Editable note content -->
<div class="detail-content">
{#if editingNote}
<textarea
@@ -426,10 +439,6 @@
</div>
{/if}
{#if selectedItem.summary}
<div class="detail-summary">{selectedItem.summary}</div>
{/if}
{#if selectedItem.tags && selectedItem.tags.length > 0}
<div class="detail-tags">
{#each selectedItem.tags as tag}
@@ -439,7 +448,7 @@
{/if}
<div class="detail-meta-line">
{#if selectedItem.folder}<span>{selectedItem.folder}</span>{/if}
{#if selectedItem.folder}<span class="meta-folder-pill">{selectedItem.folder}</span>{/if}
<span>{formatDate(selectedItem.created_at)}</span>
</div>
@@ -755,13 +764,27 @@
.close-btn:hover { background: rgba(35,26,17,0.08); color: #1e1812; }
.close-btn svg { width: 20px; height: 20px; }
.detail-screenshot {
display: block; border-radius: 14px; overflow: hidden;
margin-bottom: 16px; border: 1px solid rgba(35,26,17,0.08);
}
.detail-screenshot img {
width: 100%; height: auto; display: block; max-height: 300px; object-fit: cover;
}
.detail-screenshot:hover { opacity: 0.95; }
.detail-url {
display: block; font-size: 0.88rem; color: #8c7b69;
margin-bottom: 16px; word-break: break-all;
display: block; font-size: 0.85rem; color: #8c7b69;
margin-bottom: 14px; word-break: break-all;
text-decoration: none;
}
.detail-url:hover { color: #1e1812; text-decoration: underline; }
.meta-folder-pill {
background: rgba(35,26,17,0.06); padding: 2px 10px;
border-radius: 999px; font-weight: 600;
}
.detail-content {
margin-bottom: 20px;
}

View File

@@ -15,13 +15,16 @@ Given an item (URL, note, document, or file), you must return structured JSON wi
- folder: exactly 1 from this list: {json.dumps(FOLDERS)}
- tags: exactly 2 or 3 from this list: {json.dumps(TAGS)}
- title: a concise, normalized title (max 80 chars)
- summary: a 1-2 sentence summary of the content
- summary: a 1-2 sentence summary of the content (for links/documents only)
- corrected_text: for NOTES ONLY — return the original note text with spelling/grammar fixed. Keep the original meaning, tone, and structure. Only fix typos and obvious errors. Return empty string for non-notes.
- confidence: a float 0.0-1.0 indicating how confident you are
Rules:
- NEVER invent folders or tags not in the lists above
- NEVER skip classification
- NEVER return freeform text outside the schema
- For notes: do NOT summarize. Keep the original text. Only fix spelling.
- For notes: the summary field should be a very short 5-10 word description, not a rewrite.
- Always return valid JSON matching the schema exactly"""
RESPONSE_SCHEMA = {
@@ -41,9 +44,10 @@ RESPONSE_SCHEMA = {
},
"title": {"type": "string"},
"summary": {"type": "string"},
"corrected_text": {"type": "string"},
"confidence": {"type": "number"},
},
"required": ["folder", "tags", "title", "summary", "confidence"],
"required": ["folder", "tags", "title", "summary", "corrected_text", "confidence"],
"additionalProperties": False,
},
},

View File

@@ -111,6 +111,11 @@ async def _process_item(item_id: str):
item.title = classification.get("title") or title or "Untitled"
item.folder = classification.get("folder", "Knowledge")
item.tags = classification.get("tags", ["reference", "read-later"])
# For notes: replace raw_content with spell-corrected version
corrected = classification.get("corrected_text", "")
if item.type == "note" and corrected and corrected.strip():
item.raw_content = corrected
item.summary = classification.get("summary")
item.confidence = classification.get("confidence", 0.0)
item.extracted_text = extracted_text