106 lines
3.0 KiB
TypeScript
106 lines
3.0 KiB
TypeScript
/**
|
|
* Word-processor formats routed to the TipTap rich-text editor.
|
|
* Cell/slide/diagram formats stay on OnlyOffice.
|
|
*/
|
|
export const RICHTEXT_EXTENSIONS = new Set([
|
|
"doc",
|
|
"docm",
|
|
"docx",
|
|
"dot",
|
|
"dotm",
|
|
"dotx",
|
|
"epub",
|
|
"fb2",
|
|
"fodt",
|
|
"htm",
|
|
"html",
|
|
"hwp",
|
|
"hwpx",
|
|
"md",
|
|
"mht",
|
|
"mhtml",
|
|
"odt",
|
|
"ott",
|
|
"rtf",
|
|
"stw",
|
|
"sxw",
|
|
"txt",
|
|
"wps",
|
|
"wpt",
|
|
"xml",
|
|
"ultidoc",
|
|
] as const)
|
|
|
|
export const ULTIDOC_EXTENSION = "ultidoc.json"
|
|
|
|
const RICHTEXT_MIME_HINTS = [
|
|
"wordprocessingml",
|
|
"msword",
|
|
"opendocument.text",
|
|
"text/html",
|
|
"text/plain",
|
|
"text/markdown",
|
|
"application/rtf",
|
|
"application/epub",
|
|
] as const
|
|
|
|
export function fileExtension(name: string): string {
|
|
const base = name.split("/").pop() ?? name
|
|
const lower = base.toLowerCase()
|
|
if (lower.endsWith(`.${ULTIDOC_EXTENSION}`)) return ULTIDOC_EXTENSION
|
|
const i = base.lastIndexOf(".")
|
|
if (i <= 0) return ""
|
|
return base.slice(i + 1).toLowerCase()
|
|
}
|
|
|
|
export function isRichTextExtension(ext: string): boolean {
|
|
const normalized = ext.toLowerCase()
|
|
if (normalized === ULTIDOC_EXTENSION) return true
|
|
return RICHTEXT_EXTENSIONS.has(normalized as (typeof RICHTEXT_EXTENSIONS extends Set<infer T> ? T : never))
|
|
}
|
|
|
|
export function isRichTextMime(mime: string): boolean {
|
|
const m = mime.toLowerCase()
|
|
return RICHTEXT_MIME_HINTS.some((hint) => m.includes(hint))
|
|
}
|
|
|
|
export function isRichTextFile(file: { name: string; mime_type?: string }): boolean {
|
|
const ext = fileExtension(file.name)
|
|
if (ext && isRichTextExtension(ext)) return true
|
|
const mime = (file.mime_type ?? "").toLowerCase()
|
|
if (mime && isRichTextMime(mime)) return true
|
|
return false
|
|
}
|
|
|
|
export function isUltidocPath(path: string): boolean {
|
|
return path.toLowerCase().endsWith(`.${ULTIDOC_EXTENSION}`)
|
|
}
|
|
|
|
/** Sidecar path for a source document, e.g. /docs/report.docx → /docs/report.ultidoc.json */
|
|
export function sidecarPathForSource(sourcePath: string): string {
|
|
const normalized = sourcePath.replace(/\/+/g, "/")
|
|
const slash = normalized.lastIndexOf("/")
|
|
const dir = slash >= 0 ? normalized.slice(0, slash) : ""
|
|
const fileName = slash >= 0 ? normalized.slice(slash + 1) : normalized
|
|
const dot = fileName.lastIndexOf(".")
|
|
const base = dot > 0 ? fileName.slice(0, dot) : fileName
|
|
const sidecarName = `${base}.${ULTIDOC_EXTENSION}`
|
|
if (!dir) return `/${sidecarName}`
|
|
return `${dir}/${sidecarName}`.replace(/\/+/g, "/")
|
|
}
|
|
|
|
/** Source path from an ultidoc sidecar name (best-effort). */
|
|
export function guessSourcePathFromSidecar(sidecarPath: string, knownExtensions: string[]): string | null {
|
|
if (!isUltidocPath(sidecarPath)) return null
|
|
const normalized = sidecarPath.replace(/\/+/g, "/")
|
|
const slash = normalized.lastIndexOf("/")
|
|
const dir = slash >= 0 ? normalized.slice(0, slash) : ""
|
|
const fileName = slash >= 0 ? normalized.slice(slash + 1) : normalized
|
|
const base = fileName.slice(0, -(ULTIDOC_EXTENSION.length + 1))
|
|
for (const ext of knownExtensions) {
|
|
const candidate = `${base}.${ext}`
|
|
return dir ? `${dir}/${candidate}` : `/${candidate}`
|
|
}
|
|
return null
|
|
}
|