From 2af0a855821c8af755495a9f8f2023b4dfd9d2c9 Mon Sep 17 00:00:00 2001 From: Tim Bradgate Date: Thu, 14 May 2026 17:22:07 +0100 Subject: [PATCH] Migrate Phase 2 read-only pages to Vue 3 (Home, About, Help) - stores/help.ts: Pinia port of Vuex help module with manifest loading, document cache, and Fuse.js full-text search - components/MarkdownRenderer.vue: async marked v18 API via watch+ref; :deep() CSS selectors; import.meta.env.BASE_URL for portable help links across base paths - views/HomeView.vue: reads systemStore.currentShow/settings + userStore.currentUser; currentShowSession stubbed null until Phase 6 - views/AboutView.vue: static content port - views/HelpView.vue: BVN port with sticky sidebar, debounced search, dynamic navbar height offset - views/help/HelpDocView.vue: cache-first doc loading, watch route.params.slug - router: wire /about and /help routes; fix /help child redirect to absolute path; fix catch-all to render NotFoundView in-place (no URL redirect) matching Vue 2 behaviour Co-Authored-By: Claude Sonnet 4.6 --- client-v3/src/components/MarkdownRenderer.vue | 219 ++++++++++++++++++ client-v3/src/router/index.ts | 10 +- client-v3/src/stores/help.ts | 109 +++++++++ client-v3/src/views/AboutView.vue | 17 ++ client-v3/src/views/HelpView.vue | 88 +++++++ client-v3/src/views/HomeView.vue | 49 ++-- client-v3/src/views/help/HelpDocView.vue | 47 ++++ 7 files changed, 516 insertions(+), 23 deletions(-) create mode 100644 client-v3/src/components/MarkdownRenderer.vue create mode 100644 client-v3/src/stores/help.ts create mode 100644 client-v3/src/views/AboutView.vue create mode 100644 client-v3/src/views/HelpView.vue create mode 100644 client-v3/src/views/help/HelpDocView.vue diff --git a/client-v3/src/components/MarkdownRenderer.vue b/client-v3/src/components/MarkdownRenderer.vue new file mode 100644 index 00000000..6be9a16d --- /dev/null +++ b/client-v3/src/components/MarkdownRenderer.vue @@ -0,0 +1,219 @@ + + + + + diff --git a/client-v3/src/router/index.ts b/client-v3/src/router/index.ts index e4b2ede1..ca5ec386 100644 --- a/client-v3/src/router/index.ts +++ b/client-v3/src/router/index.ts @@ -22,7 +22,7 @@ const router = createRouter({ { path: '/about', name: 'about', - component: PlaceholderView, + component: () => import('@/views/AboutView.vue'), meta: { requiresAuth: false }, }, { @@ -124,14 +124,14 @@ const router = createRouter({ }, { path: '/help', - component: PlaceholderView, + component: () => import('@/views/HelpView.vue'), meta: { requiresAuth: false }, children: [ - { path: '', redirect: 'getting-started' }, + { path: '', redirect: '/help/getting-started' }, { name: 'help-doc', path: ':slug(.*)', - component: PlaceholderView, + component: () => import('@/views/help/HelpDocView.vue'), meta: { requiresAuth: false }, }, ], @@ -144,7 +144,7 @@ const router = createRouter({ }, { path: '/:pathMatch(.*)*', - redirect: '/404', + component: NotFoundView, }, ], }); diff --git a/client-v3/src/stores/help.ts b/client-v3/src/stores/help.ts new file mode 100644 index 00000000..c76ba219 --- /dev/null +++ b/client-v3/src/stores/help.ts @@ -0,0 +1,109 @@ +import log from 'loglevel'; +import Fuse from 'fuse.js'; +import { defineStore } from 'pinia'; + +interface HelpManifestEntry { + title: string; + slug: string; + path: string; + category: string; +} + +interface HelpState { + manifest: HelpManifestEntry[]; + documents: Record; + currentDocument: string | null; + loading: boolean; + error: string | null; + searchIndex: Fuse | null; + searchResults: HelpManifestEntry[]; +} + +export const useHelpStore = defineStore('help', { + state: (): HelpState => ({ + manifest: [], + documents: {}, + currentDocument: null, + loading: false, + error: null, + searchIndex: null, + searchResults: [], + }), + + getters: { + documentationManifest: (state) => state.manifest, + currentDocumentContent: (state) => + state.currentDocument ? state.documents[state.currentDocument] : null, + isLoading: (state) => state.loading, + searchResults: (state) => state.searchResults, + }, + + actions: { + async loadManifest() { + try { + const response = await fetch('/docs/manifest.json'); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + + const manifest: HelpManifestEntry[] = await response.json(); + this.manifest = manifest; + this.searchIndex = new Fuse(manifest, { + keys: ['title', 'path'], + threshold: 0.3, + includeScore: true, + }); + log.info(`Loaded documentation manifest with ${manifest.length} documents`); + } catch (error) { + log.error('Failed to load documentation manifest:', error); + this.error = 'Failed to load documentation manifest'; + } + }, + + async loadDocument(slug: string) { + if (this.documents[slug]) { + this.currentDocument = slug; + this.error = null; + return; + } + + this.loading = true; + const doc = this.manifest.find((d) => d.slug === slug); + + if (!doc) { + this.error = 'Document not found'; + this.loading = false; + return; + } + + try { + const response = await fetch(`/docs/${doc.path}`); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + + const content = await response.text(); + this.documents[slug] = content; + this.currentDocument = slug; + this.error = null; + } catch (error) { + log.error('Failed to load documentation:', error); + this.error = 'Failed to load documentation'; + } finally { + this.loading = false; + } + }, + + searchDocuments(query: string) { + if (!this.searchIndex) { + log.warn('Search index not initialized'); + return; + } + if (!query || query.trim() === '') { + this.searchResults = []; + return; + } + this.searchResults = this.searchIndex.search(query).map((r) => r.item); + }, + + clearSearch() { + this.searchResults = []; + }, + }, +}); diff --git a/client-v3/src/views/AboutView.vue b/client-v3/src/views/AboutView.vue new file mode 100644 index 00000000..6d38acfc --- /dev/null +++ b/client-v3/src/views/AboutView.vue @@ -0,0 +1,17 @@ + + + diff --git a/client-v3/src/views/HelpView.vue b/client-v3/src/views/HelpView.vue new file mode 100644 index 00000000..84c895a1 --- /dev/null +++ b/client-v3/src/views/HelpView.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/client-v3/src/views/HomeView.vue b/client-v3/src/views/HomeView.vue index 6f4002a6..9710f7df 100644 --- a/client-v3/src/views/HomeView.vue +++ b/client-v3/src/views/HomeView.vue @@ -1,24 +1,37 @@ - diff --git a/client-v3/src/views/help/HelpDocView.vue b/client-v3/src/views/help/HelpDocView.vue new file mode 100644 index 00000000..6bf9d584 --- /dev/null +++ b/client-v3/src/views/help/HelpDocView.vue @@ -0,0 +1,47 @@ + + +