feat(renderer): full article reader with TOC, font controls & print (M10.1)
- ArticleReader.vue: sticky TOC sidebar (desktop), mobile dropdown - Auto-generated heading anchors with IntersectionObserver tracking - Reading time estimate, font size controls (persisted localStorage) - Print mode opens clean window with serif typography - Long-form detection (>800 words + headings) shows "Read as article" - Removed isDark conditionals from ArticleDetail (dark-only app) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
aeb184b930
commit
edb4bbadcd
@@ -277,6 +277,13 @@
|
||||
>
|
||||
View brief →
|
||||
</button>
|
||||
<button
|
||||
v-if="isLongForm"
|
||||
class="text-[10px] text-accent/70 hover:text-accent transition-colors"
|
||||
@click.stop="openLongFormArticle(message.content)"
|
||||
>
|
||||
Read as article →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -364,7 +371,7 @@ function submitEdit() {
|
||||
isEditing.value = false
|
||||
editContent.value = ''
|
||||
}
|
||||
const { getContextualInlineContent, stripContentTags, stripMarkdownLinks, updatePanelFromText, panelOpen, availableTabs, setActiveTab, openFilmDetail, openBookDetail, openTVSeriesDetail, openImageDetail, openPlaceDetail, openSongDetail, openPodcastDetail, openArticleDetail, openWebsiteDetail, closeFilmDetail, closeBookDetail, closeTVSeriesDetail, closeImageDetail, closePlaceDetail, closeSongDetail, closePodcastDetail } = useContentPanel()
|
||||
const { getContextualInlineContent, stripContentTags, stripMarkdownLinks, updatePanelFromText, panelOpen, availableTabs, setActiveTab, openFilmDetail, openBookDetail, openTVSeriesDetail, openImageDetail, openPlaceDetail, openSongDetail, openPodcastDetail, openArticleDetail, openWebsiteDetail, openLongFormArticle, closeFilmDetail, closeBookDetail, closeTVSeriesDetail, closeImageDetail, closePlaceDetail, closeSongDetail, closePodcastDetail } = useContentPanel()
|
||||
const codeContext = useCodeContext()
|
||||
|
||||
const isUser = computed(() => props.message.role === 'user')
|
||||
@@ -459,6 +466,15 @@ const formattedTime = computed(() => {
|
||||
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||
})
|
||||
|
||||
// Long-form article detection (>800 words + has headings)
|
||||
const isLongForm = computed(() => {
|
||||
if (isUser.value) return false
|
||||
const text = props.message.content
|
||||
const words = text.split(/\s+/).length
|
||||
const hasHeadings = /^#{2,3}\s+.+$/m.test(text)
|
||||
return words > 800 && hasHeadings
|
||||
})
|
||||
|
||||
// Token estimate (~4 chars per token)
|
||||
const estimatedTokens = computed(() => Math.ceil(props.message.content.length / 4))
|
||||
const showTokenCount = computed(() => props.message.content.length > 20)
|
||||
|
||||
Reference in New Issue
Block a user