mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-27 11:14:59 +00:00
fix(frontend): support nested preview links
Signed-off-by: dongmucat <1127093059@qq.com>
This commit is contained in:
parent
8413ee3950
commit
0134da73b5
4 changed files with 69 additions and 7 deletions
|
|
@ -28,8 +28,12 @@ test.describe('Skill Detail Relative Links (Real API)', () => {
|
|||
extraFiles: [
|
||||
{
|
||||
path: 'docs/usage.md',
|
||||
content: '# Usage\n\nThis is linked documentation.',
|
||||
content: '# Usage\n\nThis is linked documentation.\n\n[Nested](nested.md)',
|
||||
},
|
||||
{
|
||||
path: 'docs/nested.md',
|
||||
content: '# Nested\n\nSecond-level linked documentation.',
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
|
|
@ -40,6 +44,11 @@ test.describe('Skill Detail Relative Links (Real API)', () => {
|
|||
await page.getByRole('link', { name: 'Usage' }).click()
|
||||
await expect(page.getByRole('dialog')).toContainText('usage.md')
|
||||
await expect(page.getByRole('dialog')).toContainText('This is linked documentation.')
|
||||
await expect(page.getByRole('dialog').getByRole('link', { name: 'Nested' })).toBeVisible()
|
||||
|
||||
await page.getByRole('dialog').getByRole('link', { name: 'Nested' }).click()
|
||||
await expect(page.getByRole('dialog')).toContainText('nested.md')
|
||||
await expect(page.getByRole('dialog')).toContainText('Second-level linked documentation.')
|
||||
|
||||
await page.getByRole('button', { name: 'Close' }).click()
|
||||
await expect(page.getByRole('dialog')).toBeHidden()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from 'react'
|
||||
import { useState, type MouseEvent } from 'react'
|
||||
import { Copy, Check, Download, X } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Dialog, DialogContent } from '@/shared/ui/dialog'
|
||||
|
|
@ -18,6 +18,7 @@ interface FilePreviewDialogProps {
|
|||
isLoading: boolean
|
||||
error: Error | null
|
||||
onDownload: () => void
|
||||
onLinkClick?: (href: string, event: MouseEvent<HTMLAnchorElement>) => void
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -33,6 +34,7 @@ export function FilePreviewDialog({
|
|||
isLoading,
|
||||
error,
|
||||
onDownload,
|
||||
onLinkClick,
|
||||
}: FilePreviewDialogProps) {
|
||||
const { t } = useTranslation()
|
||||
// Tracks the copy animation state: idle → spinning → done
|
||||
|
|
@ -143,7 +145,7 @@ export function FilePreviewDialog({
|
|||
<Button onClick={onDownload}>{t('filePreview.downloadHint', { name: node.name })}</Button>
|
||||
</div>
|
||||
) : content && isMarkdown ? (
|
||||
<MarkdownRenderer content={content} />
|
||||
<MarkdownRenderer content={content} onLinkClick={onLinkClick} />
|
||||
) : content && shouldHighlight ? (
|
||||
<CodeRenderer code={content} language={language} />
|
||||
) : content ? (
|
||||
|
|
|
|||
|
|
@ -127,8 +127,27 @@ vi.mock('@/features/skill/markdown-renderer', () => ({
|
|||
}))
|
||||
|
||||
vi.mock('@/features/skill/file-preview-dialog', () => ({
|
||||
FilePreviewDialog: ({ open, node }: { open: boolean; node: { path: string } | null }) => (
|
||||
open && node ? <div role="dialog">preview:{node.path}</div> : null
|
||||
FilePreviewDialog: ({
|
||||
open,
|
||||
node,
|
||||
onLinkClick,
|
||||
}: {
|
||||
open: boolean
|
||||
node: { path: string } | null
|
||||
onLinkClick?: (href: string, event: MouseEvent<HTMLAnchorElement>) => void
|
||||
}) => (
|
||||
open && node
|
||||
? (
|
||||
<div role="dialog">
|
||||
preview:{node.path}
|
||||
{onLinkClick && (
|
||||
<a href="nested.md" onClick={(event) => onLinkClick('nested.md', event)}>
|
||||
Nested
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
),
|
||||
}))
|
||||
|
||||
|
|
@ -490,6 +509,25 @@ describe('SkillDetailPage', () => {
|
|||
expect(toastMocks.error).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('resolves links inside previewed markdown files against the previewed file path', () => {
|
||||
useSkillFilesMock.mockReturnValue({
|
||||
data: [
|
||||
createSkillFile('README.md'),
|
||||
createSkillFile('docs/usage.md'),
|
||||
createSkillFile('docs/nested.md'),
|
||||
],
|
||||
})
|
||||
|
||||
render(<SkillDetailPage />)
|
||||
fireEvent.click(screen.getByRole('link', { name: 'Usage' }))
|
||||
expect(screen.getByRole('dialog').textContent).toContain('preview:docs/usage.md')
|
||||
|
||||
fireEvent.click(screen.getByRole('link', { name: 'Nested' }))
|
||||
|
||||
expect(screen.getByRole('dialog').textContent).toContain('preview:docs/nested.md')
|
||||
expect(toastMocks.error).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps the viewer on the detail page and shows a toast for missing package files', () => {
|
||||
useSkillFilesMock.mockReturnValue({
|
||||
data: [
|
||||
|
|
|
|||
|
|
@ -300,8 +300,12 @@ export function SkillDetailPage() {
|
|||
setPreviewDialogOpen(true)
|
||||
}
|
||||
|
||||
const handleOverviewLinkClick = (href: string, event: MouseEvent<HTMLAnchorElement>) => {
|
||||
const resolution = resolvePackageRelativeLink(href, documentationPath, files)
|
||||
const handlePackageMarkdownLinkClick = (
|
||||
href: string,
|
||||
event: MouseEvent<HTMLAnchorElement>,
|
||||
currentFilePath: string | null | undefined,
|
||||
) => {
|
||||
const resolution = resolvePackageRelativeLink(href, currentFilePath, files)
|
||||
|
||||
if (resolution.status === 'ignored') {
|
||||
return
|
||||
|
|
@ -318,6 +322,14 @@ export function SkillDetailPage() {
|
|||
toast.error(t('skillDetail.packageLinkMissingTitle'), t('skillDetail.packageLinkMissingDescription'))
|
||||
}
|
||||
|
||||
const handleOverviewLinkClick = (href: string, event: MouseEvent<HTMLAnchorElement>) => {
|
||||
handlePackageMarkdownLinkClick(href, event, documentationPath)
|
||||
}
|
||||
|
||||
const handlePreviewLinkClick = (href: string, event: MouseEvent<HTMLAnchorElement>) => {
|
||||
handlePackageMarkdownLinkClick(href, event, previewNode?.path)
|
||||
}
|
||||
|
||||
// Download a single file from the skill version
|
||||
const handleDownloadFile = () => {
|
||||
const isAnonymousAllowed = namespace === 'global' && skill?.visibility === 'PUBLIC'
|
||||
|
|
@ -1652,6 +1664,7 @@ export function SkillDetailPage() {
|
|||
isLoading={isLoadingPreview}
|
||||
error={previewError}
|
||||
onDownload={handleDownloadFile}
|
||||
onLinkClick={handlePreviewLinkClick}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue