catalog: add dsh-video-preview (video previewer) to the file-previewer recommended plugins (#126)
* catalog: add dsh-video-preview to the file-previewer recommended plugins Adds the video preview plugin (dsh-video-preview) to the "add preview plugin" modal catalog, with zh/en descriptions. Data integrity is guarded by tests/plugin-list.spec.ts. * test: make add-plugin-modal window.open count catalog-driven The test pinned the viewer catalog to a single entry (2 "Open:" buttons). Adding a second recommended plugin (dsh-video-preview) renders 4 buttons and broke the hardcoded assertion. Derive the expected count from builtinViewerPlugins so adding/removing plugins keeps the test honest.
This commit is contained in:
@@ -228,6 +228,7 @@ export const zh = {
|
||||
pluginOfficeDesc: '为 better-sidebar 编辑器提供 Office 三件套预览(.docx / .xlsx / .pptx),把重型 Office 渲染库拆出主包、按需安装',
|
||||
pluginSentinelDesc: '条件驱动的 agent 唤醒系统:文件/进程/端口/HTTP/命令/webhook 传感器,条件达成自动唤醒休眠会话;注册「哨兵」Tab 展示服务器全局监控表',
|
||||
pluginSidebarQaDesc: '基于 better-sidebar 的划选提问tab分页: 对话划选 → 右侧面板提问 → 同工作区独立追问会话(❓追问·主题):快速无思考模型压缩主对话上下文后与引文一起注入,不打断主对话;追问可嵌套、可继续、可归档',
|
||||
pluginVideoPreviewDesc: '在 better-sidebar 编辑器内联预览视频文件(.mp4/.webm/.mov/.mkv/.avi 等),自带支持 HTTP Range(206)的 /video 宿主路由,可拖动进度条、不受 20MB mediaLimit 限制',
|
||||
}
|
||||
|
||||
/** The en dictionary (key-set-equal to zh, enforced by the type annotation). */
|
||||
@@ -449,6 +450,7 @@ export const en: Record<keyof typeof zh, string> = {
|
||||
pluginOfficeDesc: 'Office-suite preview (.docx / .xlsx / .pptx) for the better-sidebar editor, keeping the heavy Office render libraries out of the core bundle',
|
||||
pluginSentinelDesc: 'Condition-driven agent wakeup: file/process/port/http/command/webhook sensors wake dormant sessions when conditions fire; registers a "Sentinel" tab with the server-wide watch table',
|
||||
pluginSidebarQaDesc: 'Select-and-ask: Select conversation text → ask in the right-side panel → a dedicated follow-up session (❓追问) in the same workspace; a fast no-thinking model compresses the main context and injects it with the quote, without interrupting the main conversation. Follow-ups nest, continue, and archive',
|
||||
pluginVideoPreviewDesc: 'Inline video preview (.mp4/.webm/.mov/.mkv/.avi etc.) for the better-sidebar editor, backed by a dedicated /video host route with HTTP Range (206) support — scrubbing works and files are not capped by the 20MB mediaLimit',
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,4 +20,11 @@ export const builtinViewerPlugins: readonly PluginEntry[] = [
|
||||
description: () => t('pluginOfficeDesc'),
|
||||
install: 'cd ~/.dsh && dsh plugin --profile web add @huanlin/dsh-plugin-better-sidebar-plugin-office',
|
||||
},
|
||||
{
|
||||
id: 'dsh-video-preview',
|
||||
name: '视频预览插件',
|
||||
url: 'https://github.com/zemul/dsh-video-preview',
|
||||
description: () => t('pluginVideoPreviewDesc'),
|
||||
install: 'cd ~/.dsh && dsh plugin --profile web add dsh-video-preview',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -155,7 +155,7 @@ describe('PluginListBody copy click (interactive)', () => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('both the name button and the jump button open the repo in a NEW browser tab (window.open, not a link)', () => {
|
||||
it('the name and jump buttons open each catalog entry in a NEW browser tab (window.open, not a link)', () => {
|
||||
const store = createSidebarStore()
|
||||
const service = createBetterSidebarService(store)
|
||||
const opened: Array<[string, string, string]> = []
|
||||
@@ -171,14 +171,21 @@ describe('PluginListBody copy click (interactive)', () => {
|
||||
act(() => {
|
||||
root.render(createElement(PluginListBody, { service, kind: 'viewer' }))
|
||||
})
|
||||
// Two window.open buttons per catalog entry — the name button and the
|
||||
// jump button. The expected count is catalog-driven so adding or
|
||||
// removing recommended plugins keeps the test honest instead of
|
||||
// pinning a hardcoded total.
|
||||
const jumpButtons = [...container.querySelectorAll('button[aria-label^="Open:"]')]
|
||||
expect(jumpButtons).toHaveLength(2) // the name + the jump button
|
||||
act(() => { (jumpButtons[0] as HTMLButtonElement).click() })
|
||||
act(() => { (jumpButtons[1] as HTMLButtonElement).click() })
|
||||
expect(opened).toEqual([
|
||||
[officeEntry.url, '_blank', 'noopener'],
|
||||
[officeEntry.url, '_blank', 'noopener'],
|
||||
])
|
||||
expect(jumpButtons).toHaveLength(builtinViewerPlugins.length * 2)
|
||||
for (const button of jumpButtons) {
|
||||
act(() => { (button as HTMLButtonElement).click() })
|
||||
}
|
||||
expect(opened).toEqual(
|
||||
builtinViewerPlugins.flatMap((entry) => [
|
||||
[entry.url, '_blank', 'noopener'],
|
||||
[entry.url, '_blank', 'noopener'],
|
||||
]),
|
||||
)
|
||||
act(() => { root.unmount() })
|
||||
container.remove()
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user