feat: add Office, PDF, and PPTX previews
This commit is contained in:
@@ -42,6 +42,20 @@ const pkg = JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf8')) as P
|
||||
/** The registry's strict id validation (manifest.ts / client-modules registerExternal): exactly two lowercase slash-separated segments. */
|
||||
const ID_PATTERN = /^(?!node_modules\/)(?:@[a-z0-9][a-z0-9-.]*\/[a-z0-9][a-z0-9-.]*|[a-z0-9][a-z0-9-.]*\/[a-z0-9][a-z0-9-.]*)$/
|
||||
|
||||
/** Literal require() specifiers the frozen browser module table can answer. */
|
||||
const CLIENT_REQUIRE_ALLOWED = new Set([
|
||||
'react',
|
||||
'react/jsx-runtime',
|
||||
'react-dom',
|
||||
'react-dom/client',
|
||||
'cordis',
|
||||
'@deepseek-ai/dsh-client-ui-slots',
|
||||
'@deepseek-ai/dsh-client-web-react',
|
||||
'@deepseek-ai/dsh-client-ui-primitives',
|
||||
'@deepseek-ai/dsh-client-schema-form',
|
||||
'@deepseek-ai/dsh-client-runtime/client',
|
||||
])
|
||||
|
||||
/** The registered `__ModuleLoader__.load({ id })` value of a built client bundle. */
|
||||
function bundleId(file: string): string {
|
||||
const source = readFileSync(resolve(ROOT, file), 'utf8')
|
||||
@@ -72,6 +86,16 @@ describe('registry manifest consistency (dsh.plugin.json)', () => {
|
||||
expect(registryId).not.toBe(pkg.name)
|
||||
})
|
||||
|
||||
it('client bundles require only frozen module-table entries', () => {
|
||||
for (const file of ['lib/client.js', manifest.client!.main!]) {
|
||||
const source = readFileSync(resolve(ROOT, file), 'utf8')
|
||||
// Exclude method calls such as `freeModule.require("util")`; only the
|
||||
// loader factory's lexical require() is constrained by the module table.
|
||||
const required = [...source.matchAll(/(^|[^.$\w])require\("([^"]+)"\)/gm)].map(match => match[2]!)
|
||||
expect([...new Set(required)].filter(id => !CLIENT_REQUIRE_ALLOWED.has(id))).toEqual([])
|
||||
}
|
||||
})
|
||||
|
||||
it('contributes declares no tools or skills (the host half registers none)', () => {
|
||||
expect(manifest.contributes?.tools).toEqual([])
|
||||
expect(manifest.contributes?.skills).toEqual([])
|
||||
|
||||
+6
-1
@@ -7,7 +7,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { resolve as resolvePath } from 'node:path'
|
||||
import { SettingsConflictError, settingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
import { apply } from '../src/index.ts'
|
||||
import { apply, mediaTypeForPath } from '../src/index.ts'
|
||||
import * as git from '../src/git.ts'
|
||||
import { listDirectory } from '../src/fs-tree.ts'
|
||||
import { defaultShell, PtyManager } from '../src/pty-manager.ts'
|
||||
@@ -27,6 +27,11 @@ interface FakeContext {
|
||||
}
|
||||
|
||||
describe('host plugin smoke', () => {
|
||||
it('serves PDF with the browser-native content type', () => {
|
||||
expect(mediaTypeForPath('/work/report.PDF')).toBe('application/pdf')
|
||||
expect(mediaTypeForPath('/work/archive.bin')).toBe('application/octet-stream')
|
||||
})
|
||||
|
||||
it('mounts the fenced routes', () => {
|
||||
const routes: SidebarWebRoute[] = []
|
||||
const upgrades: SidebarWebUpgradeRoute[] = []
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
import { loadPrefs, type SidebarSettingsClient } from '../src/client/prefs.ts'
|
||||
import { SIDEBAR_PREFS_DEFAULTS } from '../src/prefs-shared.ts'
|
||||
import { extOf, languageKeyForExt } from '../src/client/lang.ts'
|
||||
import { officeKindForExt } from '../src/client/office-types.ts'
|
||||
import { isPdfExt } from '../src/client/pdf-types.ts'
|
||||
import { relativeTo } from '../src/client/paths.ts'
|
||||
import { producedForClosing, resolveSidebarPath, selectProducedFiles } from '../src/client/produced-files.ts'
|
||||
import { defaultShell, ensureSpawnHelper } from '../src/pty-manager.ts'
|
||||
@@ -453,6 +455,37 @@ describe('editor language mapping', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('office preview kind', () => {
|
||||
it('routes docx/xlsx to their renderers', () => {
|
||||
expect(officeKindForExt('.docx')).toBe('docx')
|
||||
expect(officeKindForExt('.xlsx')).toBe('xlsx')
|
||||
})
|
||||
|
||||
it('routes pptx to its renderer and legacy OLE formats to download-only', () => {
|
||||
expect(officeKindForExt('.pptx')).toBe('pptx')
|
||||
expect(officeKindForExt('.doc')).toBe('download-only')
|
||||
expect(officeKindForExt('.xls')).toBe('download-only')
|
||||
expect(officeKindForExt('.ppt')).toBe('download-only')
|
||||
})
|
||||
|
||||
it('returns null for non-Office extensions and empty input', () => {
|
||||
expect(officeKindForExt('.txt')).toBeNull()
|
||||
expect(officeKindForExt('.md')).toBeNull()
|
||||
expect(officeKindForExt('.png')).toBeNull()
|
||||
expect(officeKindForExt('.pdf')).toBeNull()
|
||||
expect(officeKindForExt('')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('pdf preview kind', () => {
|
||||
it('routes only .pdf to the browser-native preview', () => {
|
||||
expect(isPdfExt('.pdf')).toBe(true)
|
||||
expect(isPdfExt('.PDF')).toBe(false)
|
||||
expect(isPdfExt('.docx')).toBe(false)
|
||||
expect(isPdfExt('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('side card preferences', () => {
|
||||
/** A fake settings wire face whose settingsGet resolves to one raw value. */
|
||||
const wire = (value: unknown): SidebarSettingsClient => ({
|
||||
|
||||
Reference in New Issue
Block a user