diff --git a/packages/app/package.json b/packages/app/package.json
index 94cf7fb5..d82eecb7 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -22,6 +22,7 @@
"@aiui/core": "workspace:*",
"@tanstack/vue-virtual": "^3.13.19",
"markdown-it": "^14.1.1",
+ "pdfjs-dist": "^5.5.207",
"pinia": "^3.0.4",
"plyr": "^3.8.4",
"vue": "^3.5.29",
diff --git a/packages/app/src/components/content/ContentPanel.vue b/packages/app/src/components/content/ContentPanel.vue
index 9d58950a..11cf495e 100644
--- a/packages/app/src/components/content/ContentPanel.vue
+++ b/packages/app/src/components/content/ContentPanel.vue
@@ -93,6 +93,12 @@
:title="longFormArticle.title"
@back="closeLongFormArticle"
/>
+
- !!(selectedFilm.value || selectedBook.value || selectedTVSeries.value || selectedSong.value || selectedPodcast.value || selectedArticle.value || selectedDesignSystemItem.value || longFormArticle.value)
+ !!(selectedFilm.value || selectedBook.value || selectedTVSeries.value || selectedSong.value || selectedPodcast.value || selectedArticle.value || selectedDesignSystemItem.value || longFormArticle.value || pdfUrl.value)
)
const windowWidth = ref(window.innerWidth)
diff --git a/packages/app/src/components/renderers/PdfViewer.vue b/packages/app/src/components/renderers/PdfViewer.vue
new file mode 100644
index 00000000..c7147e67
--- /dev/null
+++ b/packages/app/src/components/renderers/PdfViewer.vue
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
{{ title || 'PDF' }}
+
+
+
+
{{ currentPage }} / {{ totalPages }}
+
+
+
+
+
{{ Math.round(scale * 100) }}%
+
+
+
+
+
+
+
+
+
diff --git a/packages/app/src/composables/useContentPanel.ts b/packages/app/src/composables/useContentPanel.ts
index 78ceed13..b125dde5 100644
--- a/packages/app/src/composables/useContentPanel.ts
+++ b/packages/app/src/composables/useContentPanel.ts
@@ -51,6 +51,7 @@ const activeTab = ref('film')
const availableTabs = ref([])
const selectedDesignSystemItem = ref(null)
const longFormArticle = ref<{ content: string; title?: string } | null>(null)
+const pdfUrl = ref<{ url: string; title?: string } | null>(null)
export interface DesignSystemItem {
id: string
@@ -273,6 +274,7 @@ export function useContentPanel() {
selectedMagazineSection.value = null
selectedDesignSystemItem.value = null
longFormArticle.value = null
+ pdfUrl.value = null
}
function openFilmDetail(film: Film) { clearAllSelections(); selectedFilm.value = film }
@@ -296,6 +298,9 @@ export function useContentPanel() {
function openLongFormArticle(content: string, title?: string) { clearAllSelections(); longFormArticle.value = { content, title }; panelOpen.value = true }
function closeLongFormArticle() { longFormArticle.value = null }
+ function openPdfViewer(url: string, title?: string) { clearAllSelections(); pdfUrl.value = { url, title }; panelOpen.value = true }
+ function closePdfViewer() { pdfUrl.value = null }
+
function openMagazineSectionDetail(section: MagazineSection, index: number) {
clearAllSelections()
selectedMagazineSection.value = section
@@ -443,6 +448,9 @@ export function useContentPanel() {
longFormArticle,
openLongFormArticle,
closeLongFormArticle,
+ pdfUrl,
+ openPdfViewer,
+ closePdfViewer,
enterDesignSystemMode,
closePanel,
showAllFilms,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 14e55265..769e13bf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,6 +7,10 @@ settings:
importers:
.:
+ dependencies:
+ pdfjs-dist:
+ specifier: ^5.5.207
+ version: 5.5.207
devDependencies:
turbo:
specifier: ^2.8.12
@@ -26,6 +30,9 @@ importers:
markdown-it:
specifier: ^14.1.1
version: 14.1.1
+ pdfjs-dist:
+ specifier: ^5.5.207
+ version: 5.5.207
pinia:
specifier: ^3.0.4
version: 3.0.4(typescript@5.8.3)(vue@3.5.29(typescript@5.8.3))
@@ -867,6 +874,81 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@napi-rs/canvas-android-arm64@0.1.95':
+ resolution: {integrity: sha512-SqTh0wsYbetckMXEvHqmR7HKRJujVf1sYv1xdlhkifg6TlCSysz1opa49LlS3+xWuazcQcfRfmhA07HxxxGsAA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@napi-rs/canvas-darwin-arm64@0.1.95':
+ resolution: {integrity: sha512-F7jT0Syu+B9DGBUBcMk3qCRIxAWiDXmvEjamwbYfbZl7asI1pmXZUnCOoIu49Wt0RNooToYfRDxU9omD6t5Xuw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@napi-rs/canvas-darwin-x64@0.1.95':
+ resolution: {integrity: sha512-54eb2Ho15RDjYGXO/harjRznBrAvu+j5nQ85Z4Qd6Qg3slR8/Ja+Yvvy9G4yo7rdX6NR9GPkZeSTf2UcKXwaXw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@napi-rs/canvas-linux-arm-gnueabihf@0.1.95':
+ resolution: {integrity: sha512-hYaLCSLx5bmbnclzQc3ado3PgZ66blJWzjXp0wJmdwpr/kH+Mwhj6vuytJIomgksyJoCdIqIa4N6aiqBGJtJ5Q==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@napi-rs/canvas-linux-arm64-gnu@0.1.95':
+ resolution: {integrity: sha512-J7VipONahKsmScPZsipHVQBqpbZx4favaD8/enWzzlGcjiwycOoymL7f4tNeqdjK0su19bDOUt6mjp9gsPWYlw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@napi-rs/canvas-linux-arm64-musl@0.1.95':
+ resolution: {integrity: sha512-PXy0UT1J/8MPG8UAkWp6Fd51ZtIZINFzIjGH909JjQrtCuJf3X6nanHYdz1A+Wq9o4aoPAw1YEUpFS1lelsVlg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@napi-rs/canvas-linux-riscv64-gnu@0.1.95':
+ resolution: {integrity: sha512-2IzCkW2RHRdcgF9W5/plHvYFpc6uikyjMb5SxjqmNxfyDFz9/HB89yhi8YQo0SNqrGRI7yBVDec7Pt+uMyRWsg==}
+ engines: {node: '>= 10'}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@napi-rs/canvas-linux-x64-gnu@0.1.95':
+ resolution: {integrity: sha512-OV/ol/OtcUr4qDhQg8G7SdViZX8XyQeKpPsVv/j3+7U178FGoU4M+yIocdVo1ih/A8GQ63+LjF4jDoEjaVU8Pw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@napi-rs/canvas-linux-x64-musl@0.1.95':
+ resolution: {integrity: sha512-Z5KzqBK/XzPz5+SFHKz7yKqClEQ8pOiEDdgk5SlphBLVNb8JFIJkxhtJKSvnJyHh2rjVgiFmvtJzMF0gNwwKyQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@napi-rs/canvas-win32-arm64-msvc@0.1.95':
+ resolution: {integrity: sha512-aj0YbRpe8qVJ4OzMsK7NfNQePgcf9zkGFzNZ9mSuaxXzhpLHmlF2GivNdCdNOg8WzA/NxV6IU4c5XkXadUMLeA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@napi-rs/canvas-win32-x64-msvc@0.1.95':
+ resolution: {integrity: sha512-GA8leTTCfdjuHi8reICTIxU0081PhXvl3lzIniLUjeLACx9GubUiyzkwFb+oyeKLS5IAGZFLKnzAf4wm2epRlA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@napi-rs/canvas@0.1.95':
+ resolution: {integrity: sha512-lkg23ge+rgyhgUwXmlbkPEhuhHq/hUi/gXKH+4I7vO+lJrbNfEYcQdJLIGjKyXLQzgFiiyDAwh5vAe/tITAE+w==}
+ engines: {node: '>= 10'}
+
'@playwright/test@1.58.2':
resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==}
engines: {node: '>=18'}
@@ -2250,6 +2332,9 @@ packages:
engines: {node: '>= 4.4.x'}
hasBin: true
+ node-readable-to-web-readable-stream@0.4.2:
+ resolution: {integrity: sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==}
+
node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
@@ -2311,6 +2396,10 @@ packages:
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+ pdfjs-dist@5.5.207:
+ resolution: {integrity: sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==}
+ engines: {node: '>=20.19.0 || >=22.13.0 || >=24'}
+
perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
@@ -3852,6 +3941,54 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@napi-rs/canvas-android-arm64@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-darwin-arm64@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-darwin-x64@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-linux-arm-gnueabihf@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-linux-arm64-gnu@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-linux-arm64-musl@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-linux-riscv64-gnu@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-linux-x64-gnu@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-linux-x64-musl@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-win32-arm64-msvc@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas-win32-x64-msvc@0.1.95':
+ optional: true
+
+ '@napi-rs/canvas@0.1.95':
+ optionalDependencies:
+ '@napi-rs/canvas-android-arm64': 0.1.95
+ '@napi-rs/canvas-darwin-arm64': 0.1.95
+ '@napi-rs/canvas-darwin-x64': 0.1.95
+ '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.95
+ '@napi-rs/canvas-linux-arm64-gnu': 0.1.95
+ '@napi-rs/canvas-linux-arm64-musl': 0.1.95
+ '@napi-rs/canvas-linux-riscv64-gnu': 0.1.95
+ '@napi-rs/canvas-linux-x64-gnu': 0.1.95
+ '@napi-rs/canvas-linux-x64-musl': 0.1.95
+ '@napi-rs/canvas-win32-arm64-msvc': 0.1.95
+ '@napi-rs/canvas-win32-x64-msvc': 0.1.95
+ optional: true
+
'@playwright/test@1.58.2':
dependencies:
playwright: 1.58.2
@@ -5280,6 +5417,9 @@ snapshots:
iconv-lite: 0.6.3
sax: 1.5.0
+ node-readable-to-web-readable-stream@0.4.2:
+ optional: true
+
node-releases@2.0.27: {}
nth-check@2.1.1:
@@ -5341,6 +5481,11 @@ snapshots:
pathe@2.0.3: {}
+ pdfjs-dist@5.5.207:
+ optionalDependencies:
+ '@napi-rs/canvas': 0.1.95
+ node-readable-to-web-readable-stream: 0.4.2
+
perfect-debounce@1.0.0: {}
perfect-debounce@2.1.0: {}