code.ts 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import { computed, ref } from 'vue'
  2. type PreviewStatus = 'idle' | 'downloading' | 'rendering' | 'ready' | 'error'
  3. type PreviewKind = 'document' | 'sheet' | 'drawing' | 'source' | 'media' | 'unknown'
  4. interface PreviewSource {
  5. id: string
  6. filename: string
  7. url: string
  8. size?: number
  9. updatedAt?: string
  10. }
  11. interface RenderPlan {
  12. kind: PreviewKind
  13. extension: string
  14. lazyChunk: string
  15. preferredInput: 'url' | 'file'
  16. }
  17. const chunkMap: Record<string, RenderPlan['kind']> = {
  18. doc: 'document',
  19. docx: 'document',
  20. pdf: 'document',
  21. ofd: 'document',
  22. typ: 'document',
  23. typst: 'document',
  24. xlsx: 'sheet',
  25. csv: 'sheet',
  26. dxf: 'drawing',
  27. dwg: 'drawing',
  28. ts: 'source',
  29. json: 'source',
  30. mp4: 'media'
  31. }
  32. export const activeSource = ref<PreviewSource>({
  33. id: 'sample-typst',
  34. filename: 'report.typ',
  35. url: '/example/report.typ',
  36. updatedAt: '2026-06-06T17:40:00+08:00'
  37. })
  38. export const status = ref<PreviewStatus>('idle')
  39. export const statusText = computed(() => `${activeSource.value.filename} / ${status.value}`)
  40. function getExtension(filename: string) {
  41. const dotIndex = filename.lastIndexOf('.')
  42. return dotIndex >= 0 ? filename.slice(dotIndex + 1).toLowerCase() : ''
  43. }
  44. export function createRenderPlan(source: PreviewSource): RenderPlan {
  45. const extension = getExtension(source.filename)
  46. const kind = chunkMap[extension] || 'unknown'
  47. return {
  48. kind,
  49. extension,
  50. lazyChunk: kind === 'unknown' ? 'fallback' : `${kind}-${extension}`,
  51. preferredInput: source.url.includes('/download') ? 'file' : 'url'
  52. }
  53. }
  54. export async function openPreview(source: PreviewSource) {
  55. activeSource.value = source
  56. status.value = 'downloading'
  57. const response = await fetch(source.url, { credentials: 'include' })
  58. if (!response.ok) {
  59. status.value = 'error'
  60. throw new Error(`Download failed: ${response.status}`)
  61. }
  62. status.value = 'rendering'
  63. const blob = await response.blob()
  64. status.value = 'ready'
  65. return new File([blob], source.filename, { type: blob.type })
  66. }