mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
feat(desktop): add classifyPreviewLink classifier
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
36a3e4c018
commit
8c4e8073ab
29
desktop/src/lib/previewLinkRouter.test.ts
Normal file
29
desktop/src/lib/previewLinkRouter.test.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { classifyPreviewLink } from './previewLinkRouter'
|
||||
|
||||
describe('classifyPreviewLink', () => {
|
||||
it('classifies loopback urls as browser-localhost', () => {
|
||||
expect(classifyPreviewLink('http://localhost:5173/').kind).toBe('browser-localhost')
|
||||
expect(classifyPreviewLink('http://127.0.0.1:8080/x').kind).toBe('browser-localhost')
|
||||
})
|
||||
it('classifies html file paths as browser-file', () => {
|
||||
expect(classifyPreviewLink('file:///Users/x/index.html').kind).toBe('browser-file')
|
||||
expect(classifyPreviewLink('/Users/x/page.htm').kind).toBe('browser-file')
|
||||
expect(classifyPreviewLink('./out/index.html').kind).toBe('browser-file')
|
||||
})
|
||||
it('classifies relative previewable docs as file-preview', () => {
|
||||
expect(classifyPreviewLink('docs/report.md').kind).toBe('file-preview')
|
||||
expect(classifyPreviewLink('src/app.ts').kind).toBe('file-preview')
|
||||
})
|
||||
it('classifies remote http(s) as remote', () => {
|
||||
expect(classifyPreviewLink('https://example.com').kind).toBe('remote')
|
||||
})
|
||||
it('ignores anchors and empty', () => {
|
||||
expect(classifyPreviewLink('#section').kind).toBe('ignored')
|
||||
expect(classifyPreviewLink('').kind).toBe('ignored')
|
||||
})
|
||||
it('exposes a normalized path for file kinds', () => {
|
||||
expect(classifyPreviewLink('file:///Users/x/index.html').path).toBe('/Users/x/index.html')
|
||||
expect(classifyPreviewLink('docs/report.md').path).toBe('docs/report.md')
|
||||
})
|
||||
})
|
||||
45
desktop/src/lib/previewLinkRouter.ts
Normal file
45
desktop/src/lib/previewLinkRouter.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { isLoopbackHostname } from './desktopRuntime'
|
||||
|
||||
export type PreviewLinkKind =
|
||||
| 'browser-localhost'
|
||||
| 'browser-file'
|
||||
| 'file-preview'
|
||||
| 'remote'
|
||||
| 'ignored'
|
||||
|
||||
export type PreviewLinkClass = { kind: PreviewLinkKind; path?: string; url?: string }
|
||||
|
||||
const PREVIEWABLE_EXT = new Set(['md', 'markdown', 'txt', 'ts', 'tsx', 'js', 'jsx', 'json', 'css', 'py', 'rs', 'go', 'java', 'sh', 'yml', 'yaml', 'png', 'jpg', 'jpeg', 'gif', 'svg'])
|
||||
|
||||
function extOf(p: string): string {
|
||||
const m = /\.([a-z0-9]+)(?:[?#].*)?$/i.exec(p)
|
||||
const g = m?.[1]
|
||||
return g ? g.toLowerCase() : ''
|
||||
}
|
||||
|
||||
export function classifyPreviewLink(href: string): PreviewLinkClass {
|
||||
const raw = href.trim()
|
||||
if (!raw || raw.startsWith('#')) return { kind: 'ignored' }
|
||||
|
||||
try {
|
||||
const u = new URL(raw)
|
||||
if (u.protocol === 'http:' || u.protocol === 'https:') {
|
||||
return isLoopbackHostname(u.hostname)
|
||||
? { kind: 'browser-localhost', url: u.toString() }
|
||||
: { kind: 'remote', url: u.toString() }
|
||||
}
|
||||
if (u.protocol === 'file:') {
|
||||
return { kind: 'browser-file', path: decodeURIComponent(u.pathname) }
|
||||
}
|
||||
return { kind: 'ignored' }
|
||||
} catch {
|
||||
// not an absolute URL → treat as a path
|
||||
}
|
||||
|
||||
const ext = extOf(raw)
|
||||
if (ext === 'html' || ext === 'htm') return { kind: 'browser-file', path: raw }
|
||||
if (PREVIEWABLE_EXT.has(ext)) {
|
||||
return raw.startsWith('/') ? { kind: 'browser-file', path: raw } : { kind: 'file-preview', path: raw }
|
||||
}
|
||||
return { kind: 'ignored' }
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user