feat(attachments): 全面扩展笔记附件专属图标与标签类型体系
- 细分支持各平台原生安装包 (APK, EXE, DMG, Linux 包) - 新增设计工程 (AI/PSD/FIG/SKETCH) 与 3D/CAD 建模 (BLEND/STL/DWG) - 新增终端运维自动化脚本 (SH/PS1/BAT) 与系统日志排障 (LOG/CRASH) - 新增证书密钥安全凭据 (CRT/PEM/PUB/KEY) 与思维导图架构 (XMIND/DRAWIO) - 新增电子书 (EPUB/MOBI)、字体资源 (WOFF2/TTF)、磁盘镜像 (ISO) 与数据库 (DB) - 增加大写扩展名精准徽标输出与 .key 密钥/Keynote 消歧 - 跨端同步更新 Web、iOS 原生 EditorBundle 与 Android/Mobile 编辑器样式 - 补全单元测试与格式映射断言
This commit is contained in:
1 parent
d2ddb07a0f
commit
fbb41aee9d
8 files changed
+977
-703
No files matched your search
+342
-342
File diff suppressed because it is too large.
Load diff
Vendored
+342
-342
File diff suppressed because it is too large.
Load diff
@@ -611,8 +611,16 @@ html[data-theme="dark"] .ProseMirror a:active {
|
||||
.ProseMirror a.edgeever-attachment-kind-code::before { background: #faf5ff; color: #8b5cf6; content: "</>"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-text::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-text::before { background: #f1f5f9; color: #64748b; content: "TXT"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-apk::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-apk::before { background: #ecfdf5; color: #059669; content: "APK"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-exe::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-exe::before { background: #f0f9ff; color: #0284c7; content: "EXE"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-dmg::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-dmg::before { background: #f5f3ff; color: #7c3aed; content: "DMG"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-linux::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-linux::before { background: #fff7ed; color: #ea580c; content: "LINUX"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-executable::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-executable::before { background: #ccfbf1; color: #0d9488; content: "PKG"; }
|
||||
.ProseMirror a.edgeever-attachment-kind-executable::before { background: #ccfbf1; color: #0d9488; content: "BIN"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-book::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-book::before { background: #fef3c7; color: #b45309; content: "BOOK"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-font::before,
|
||||
@@ -621,6 +629,18 @@ html[data-theme="dark"] .ProseMirror a:active {
|
||||
.ProseMirror a.edgeever-attachment-kind-diskimage::before { background: #cffafe; color: #0891b2; content: "ISO"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-database::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-database::before { background: #fae8ff; color: #c026d3; content: "DB"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-design::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-design::before { background: #fdf2f8; color: #db2777; content: "DESIGN"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-model3d::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-model3d::before { background: #eff6ff; color: #2563eb; content: "3D"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-script::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-script::before { background: #ecfdf5; color: #059669; content: "SHELL"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-log::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-log::before { background: #f4f4f5; color: #71717a; content: "LOG"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-certificate::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-certificate::before { background: #fefce8; color: #ca8a04; content: "KEY"; }
|
||||
.edgeever-prose a.edgeever-attachment-kind-diagram::before,
|
||||
.ProseMirror a.edgeever-attachment-kind-diagram::before { background: #f0fdfa; color: #0d9488; content: "DIAG"; }
|
||||
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-link,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-link,
|
||||
@@ -653,6 +673,14 @@ html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-code::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-code::before { background: #581c87; color: #d8b4fe; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-text::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-text::before { background: #334155; color: #cbd5e1; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-apk::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-apk::before { background: #064e3b; color: #6ee7b7; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-exe::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-exe::before { background: #0c4a6e; color: #7dd3fc; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-dmg::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-dmg::before { background: #4c1d95; color: #c4b5fd; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-linux::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-linux::before { background: #7c2d12; color: #fdba74; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-executable::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-executable::before { background: #134e4a; color: #5eead4; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-book::before,
|
||||
@@ -663,6 +691,18 @@ html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-diskimage::be
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-diskimage::before { background: #164e63; color: #67e8f9; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-database::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-database::before { background: #701a75; color: #f0abfc; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-design::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-design::before { background: #831843; color: #f472b6; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-model3d::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-model3d::before { background: #1e3a8a; color: #93c5fd; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-script::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-script::before { background: #064e3b; color: #6ee7b7; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-log::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-log::before { background: #27272a; color: #a1a1aa; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-certificate::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-certificate::before { background: #713f12; color: #fde047; }
|
||||
html[data-theme="dark"] .edgeever-prose a.edgeever-attachment-kind-diagram::before,
|
||||
html[data-theme="dark"] .ProseMirror a.edgeever-attachment-kind-diagram::before { background: #134e4a; color: #5eead4; }
|
||||
|
||||
.edgeever-prose .edgeever-unsupported-content,
|
||||
.ProseMirror .edgeever-unsupported-content {
|
||||
|
||||
@@ -3236,11 +3236,21 @@ const getEditorStyles = (theme: "light" | "dark", options?: { viewer?: boolean }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-archive::before { background: ${theme === "dark" ? "#713f12" : "#fffbeb"}; color: ${theme === "dark" ? "#fde68a" : "#d97706"}; content: "ZIP"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-code::before { background: ${theme === "dark" ? "#581c87" : "#faf5ff"}; color: ${theme === "dark" ? "#d8b4fe" : "#8b5cf6"}; content: "</>"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-text::before { background: ${theme === "dark" ? "#334155" : "#f1f5f9"}; color: ${theme === "dark" ? "#cbd5e1" : "#64748b"}; content: "TXT"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-executable::before { background: ${theme === "dark" ? "#134e4a" : "#ccfbf1"}; color: ${theme === "dark" ? "#5eead4" : "#0d9488"}; content: "PKG"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-apk::before { background: ${theme === "dark" ? "#064e3b" : "#ecfdf5"}; color: ${theme === "dark" ? "#6ee7b7" : "#059669"}; content: "APK"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-exe::before { background: ${theme === "dark" ? "#0c4a6e" : "#f0f9ff"}; color: ${theme === "dark" ? "#7dd3fc" : "#0284c7"}; content: "EXE"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-dmg::before { background: ${theme === "dark" ? "#4c1d95" : "#f5f3ff"}; color: ${theme === "dark" ? "#c4b5fd" : "#7c3aed"}; content: "DMG"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-linux::before { background: ${theme === "dark" ? "#7c2d12" : "#fff7ed"}; color: ${theme === "dark" ? "#fdba74" : "#ea580c"}; content: "LINUX"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-executable::before { background: ${theme === "dark" ? "#134e4a" : "#ccfbf1"}; color: ${theme === "dark" ? "#5eead4" : "#0d9488"}; content: "BIN"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-book::before { background: ${theme === "dark" ? "#78350f" : "#fef3c7"}; color: ${theme === "dark" ? "#fcd34d" : "#b45309"}; content: "BOOK"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-font::before { background: ${theme === "dark" ? "#312e81" : "#e0e7ff"}; color: ${theme === "dark" ? "#a5b4fc" : "#4f46e5"}; content: "FONT"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-diskimage::before { background: ${theme === "dark" ? "#164e63" : "#cffafe"}; color: ${theme === "dark" ? "#67e8f9" : "#0891b2"}; content: "ISO"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-database::before { background: ${theme === "dark" ? "#701a75" : "#fae8ff"}; color: ${theme === "dark" ? "#f0abfc" : "#c026d3"}; content: "DB"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-design::before { background: ${theme === "dark" ? "#831843" : "#fdf2f8"}; color: ${theme === "dark" ? "#f472b6" : "#db2777"}; content: "DESIGN"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-model3d::before { background: ${theme === "dark" ? "#1e3a8a" : "#eff6ff"}; color: ${theme === "dark" ? "#93c5fd" : "#2563eb"}; content: "3D"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-script::before { background: ${theme === "dark" ? "#064e3b" : "#ecfdf5"}; color: ${theme === "dark" ? "#6ee7b7" : "#059669"}; content: "SHELL"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-log::before { background: ${theme === "dark" ? "#27272a" : "#f4f4f5"}; color: ${theme === "dark" ? "#a1a1aa" : "#71717a"}; content: "LOG"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-certificate::before { background: ${theme === "dark" ? "#713f12" : "#fefce8"}; color: ${theme === "dark" ? "#fde047" : "#ca8a04"}; content: "KEY"; }
|
||||
.edgeever-editor-content a.edgeever-attachment-kind-diagram::before { background: ${theme === "dark" ? "#134e4a" : "#f0fdfa"}; color: ${theme === "dark" ? "#5eead4" : "#0d9488"}; content: "DIAG"; }
|
||||
.edgeever-editor-content .edgeever-unsupported-content {
|
||||
border: 1px dashed ${theme === "dark" ? "#64748b" : "#94a3b8"};
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import {
|
||||
AppWindow,
|
||||
BookOpen,
|
||||
Box,
|
||||
Cpu,
|
||||
Database,
|
||||
Disc,
|
||||
File,
|
||||
@@ -8,9 +11,16 @@ import {
|
||||
FileSpreadsheet,
|
||||
FileText,
|
||||
ImageIcon,
|
||||
KeyRound,
|
||||
Music,
|
||||
Network,
|
||||
Package,
|
||||
Palette,
|
||||
Presentation,
|
||||
Scroll,
|
||||
Smartphone,
|
||||
SquareTerminal,
|
||||
Terminal,
|
||||
Type,
|
||||
Video,
|
||||
} from "lucide-react";
|
||||
@@ -39,12 +49,22 @@ export const AttachmentFileIcon = ({
|
||||
case "presentation": return <Presentation className={cn("text-orange-500", commonClassName)} aria-hidden="true" />;
|
||||
case "archive": return <FileArchive className={cn("text-amber-500", commonClassName)} aria-hidden="true" />;
|
||||
case "code": return <FileCode2 className={cn("text-violet-500", commonClassName)} aria-hidden="true" />;
|
||||
case "script": return <SquareTerminal className={cn("text-emerald-600", commonClassName)} aria-hidden="true" />;
|
||||
case "text": return <FileText className={cn("text-slate-500", commonClassName)} aria-hidden="true" />;
|
||||
case "executable": return <Package className={cn("text-teal-600", commonClassName)} aria-hidden="true" />;
|
||||
case "apk": return <Smartphone className={cn("text-emerald-500", commonClassName)} aria-hidden="true" />;
|
||||
case "exe": return <AppWindow className={cn("text-sky-500", commonClassName)} aria-hidden="true" />;
|
||||
case "dmg": return <Package className={cn("text-violet-500", commonClassName)} aria-hidden="true" />;
|
||||
case "linux": return <Terminal className={cn("text-orange-500", commonClassName)} aria-hidden="true" />;
|
||||
case "executable": return <Cpu className={cn("text-teal-600", commonClassName)} aria-hidden="true" />;
|
||||
case "book": return <BookOpen className={cn("text-amber-700", commonClassName)} aria-hidden="true" />;
|
||||
case "font": return <Type className={cn("text-indigo-500", commonClassName)} aria-hidden="true" />;
|
||||
case "diskimage": return <Disc className={cn("text-cyan-600", commonClassName)} aria-hidden="true" />;
|
||||
case "database": return <Database className={cn("text-fuchsia-600", commonClassName)} aria-hidden="true" />;
|
||||
case "design": return <Palette className={cn("text-pink-500", commonClassName)} aria-hidden="true" />;
|
||||
case "model3d": return <Box className={cn("text-blue-500", commonClassName)} aria-hidden="true" />;
|
||||
case "log": return <Scroll className={cn("text-zinc-500", commonClassName)} aria-hidden="true" />;
|
||||
case "certificate": return <KeyRound className={cn("text-amber-600", commonClassName)} aria-hidden="true" />;
|
||||
case "diagram": return <Network className={cn("text-teal-600", commonClassName)} aria-hidden="true" />;
|
||||
default: return <File className={cn("text-slate-400", commonClassName)} aria-hidden="true" />;
|
||||
}
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
resolvePlayableMediaMimeType,
|
||||
resolveVideoMimeType,
|
||||
} from "./attachment-kind.ts";
|
||||
import { getAttachmentTypeLabel } from "./attachment-metadata.ts";
|
||||
|
||||
describe("attachment kind", () => {
|
||||
test("uses MIME types for media and filenames for common documents", () => {
|
||||
@@ -42,15 +43,34 @@ describe("attachment kind", () => {
|
||||
expect(resolvePlayableMediaMimeType("application/octet-stream", "voice.mp3")).toBe("audio/mpeg");
|
||||
});
|
||||
|
||||
test("identifies executables, books, fonts, disk images, and databases", () => {
|
||||
expect(resolveAttachmentKind("application/vnd.android.package-archive", "app.apk")).toBe("executable");
|
||||
expect(resolveAttachmentKind("application/octet-stream", "EdgeEver-Setup.exe")).toBe("executable");
|
||||
expect(resolveAttachmentKind(null, "EdgeEver-arm64.dmg")).toBe("executable");
|
||||
expect(resolveAttachmentKind(null, "installer.msi")).toBe("executable");
|
||||
expect(resolveAttachmentKind(null, "package.deb")).toBe("executable");
|
||||
expect(resolveAttachmentKind(null, "package.rpm")).toBe("executable");
|
||||
expect(resolveAttachmentKind(null, "app.AppImage")).toBe("executable");
|
||||
expect(resolveAttachmentKind("application/zip", "app.apk")).toBe("executable");
|
||||
test("identifies platform executables, books, fonts, disk images, and databases", () => {
|
||||
expect(resolveAttachmentKind("application/vnd.android.package-archive", "app.apk")).toBe("apk");
|
||||
expect(resolveAttachmentKind("application/zip", "app.apk")).toBe("apk");
|
||||
expect(resolveAttachmentKind(null, "app.aab")).toBe("apk");
|
||||
expect(getAttachmentTypeLabel(null, "app.apk")).toBe("APK");
|
||||
expect(getAttachmentTypeLabel(null, "app.aab")).toBe("AAB");
|
||||
|
||||
expect(resolveAttachmentKind("application/octet-stream", "EdgeEver-Setup.exe")).toBe("exe");
|
||||
expect(resolveAttachmentKind(null, "installer.msi")).toBe("exe");
|
||||
expect(getAttachmentTypeLabel(null, "EdgeEver-Setup.exe")).toBe("EXE");
|
||||
expect(getAttachmentTypeLabel(null, "installer.msi")).toBe("MSI");
|
||||
|
||||
expect(resolveAttachmentKind(null, "EdgeEver-arm64.dmg")).toBe("dmg");
|
||||
expect(resolveAttachmentKind(null, "bundle.pkg")).toBe("dmg");
|
||||
expect(resolveAttachmentKind(null, "release.ipa")).toBe("dmg");
|
||||
expect(getAttachmentTypeLabel(null, "EdgeEver-arm64.dmg")).toBe("DMG");
|
||||
expect(getAttachmentTypeLabel(null, "bundle.pkg")).toBe("PKG");
|
||||
expect(getAttachmentTypeLabel(null, "release.ipa")).toBe("IPA");
|
||||
|
||||
expect(resolveAttachmentKind(null, "package.deb")).toBe("linux");
|
||||
expect(resolveAttachmentKind(null, "package.rpm")).toBe("linux");
|
||||
expect(resolveAttachmentKind(null, "app.AppImage")).toBe("linux");
|
||||
expect(getAttachmentTypeLabel(null, "package.deb")).toBe("DEB");
|
||||
expect(getAttachmentTypeLabel(null, "package.rpm")).toBe("RPM");
|
||||
expect(getAttachmentTypeLabel(null, "app.AppImage")).toBe("APPIMAGE");
|
||||
|
||||
expect(resolveAttachmentKind("application/x-executable", "payload.bin")).toBe("executable");
|
||||
expect(getAttachmentTypeLabel("application/x-executable", "payload.bin")).toBe("BIN");
|
||||
|
||||
expect(resolveAttachmentKind("application/epub+zip", "manual.epub")).toBe("book");
|
||||
expect(resolveAttachmentKind(null, "novel.mobi")).toBe("book");
|
||||
@@ -72,6 +92,56 @@ describe("attachment kind", () => {
|
||||
expect(resolveAttachmentKind(null, "main.go")).toBe("code");
|
||||
expect(resolveAttachmentKind(null, "Main.java")).toBe("code");
|
||||
expect(resolveAttachmentKind(null, "script.py")).toBe("code");
|
||||
|
||||
// Scripts
|
||||
expect(resolveAttachmentKind(null, "deploy.sh")).toBe("script");
|
||||
expect(resolveAttachmentKind(null, "run.bash")).toBe("script");
|
||||
expect(resolveAttachmentKind(null, "install.ps1")).toBe("script");
|
||||
expect(resolveAttachmentKind(null, "build.bat")).toBe("script");
|
||||
expect(getAttachmentTypeLabel(null, "deploy.sh")).toBe("SH");
|
||||
expect(getAttachmentTypeLabel(null, "install.ps1")).toBe("PS1");
|
||||
|
||||
// Design
|
||||
expect(resolveAttachmentKind(null, "ui.sketch")).toBe("design");
|
||||
expect(resolveAttachmentKind(null, "banner.psd")).toBe("design");
|
||||
expect(resolveAttachmentKind(null, "icon.ai")).toBe("design");
|
||||
expect(resolveAttachmentKind(null, "mockup.fig")).toBe("design");
|
||||
expect(getAttachmentTypeLabel(null, "banner.psd")).toBe("PSD");
|
||||
expect(getAttachmentTypeLabel(null, "icon.ai")).toBe("AI");
|
||||
expect(getAttachmentTypeLabel(null, "mockup.fig")).toBe("FIG");
|
||||
|
||||
// 3D & CAD
|
||||
expect(resolveAttachmentKind(null, "character.blend")).toBe("model3d");
|
||||
expect(resolveAttachmentKind(null, "part.stl")).toBe("model3d");
|
||||
expect(resolveAttachmentKind(null, "mesh.obj")).toBe("model3d");
|
||||
expect(resolveAttachmentKind(null, "floorplan.dwg")).toBe("model3d");
|
||||
expect(getAttachmentTypeLabel(null, "character.blend")).toBe("BLEND");
|
||||
expect(getAttachmentTypeLabel(null, "part.stl")).toBe("STL");
|
||||
|
||||
// Logs
|
||||
expect(resolveAttachmentKind(null, "server.log")).toBe("log");
|
||||
expect(resolveAttachmentKind(null, "panic.crash")).toBe("log");
|
||||
expect(getAttachmentTypeLabel(null, "server.log")).toBe("LOG");
|
||||
|
||||
// Certificates & Keys
|
||||
expect(resolveAttachmentKind(null, "cert.pem")).toBe("certificate");
|
||||
expect(resolveAttachmentKind(null, "server.crt")).toBe("certificate");
|
||||
expect(resolveAttachmentKind(null, "id_rsa.pub")).toBe("certificate");
|
||||
expect(resolveAttachmentKind(null, "private.key")).toBe("certificate");
|
||||
expect(getAttachmentTypeLabel(null, "cert.pem")).toBe("PEM");
|
||||
expect(getAttachmentTypeLabel(null, "id_rsa.pub")).toBe("PUB");
|
||||
|
||||
// Diagrams & Mindmaps
|
||||
expect(resolveAttachmentKind(null, "roadmap.xmind")).toBe("diagram");
|
||||
expect(resolveAttachmentKind(null, "architecture.drawio")).toBe("diagram");
|
||||
expect(resolveAttachmentKind(null, "whiteboard.excalidraw")).toBe("diagram");
|
||||
expect(getAttachmentTypeLabel(null, "roadmap.xmind")).toBe("XMIND");
|
||||
expect(getAttachmentTypeLabel(null, "architecture.drawio")).toBe("DRAWIO");
|
||||
|
||||
// Common office/data extensions
|
||||
expect(getAttachmentTypeLabel(null, "table.csv")).toBe("CSV");
|
||||
expect(getAttachmentTypeLabel(null, "README.md")).toBe("MD");
|
||||
expect(getAttachmentTypeLabel(null, "notes.txt")).toBe("TXT");
|
||||
});
|
||||
|
||||
test("falls back predictably for text and unknown files", () => {
|
||||
|
||||
@@ -8,12 +8,22 @@ export const ATTACHMENT_KINDS = [
|
||||
"presentation",
|
||||
"archive",
|
||||
"code",
|
||||
"script",
|
||||
"text",
|
||||
"apk",
|
||||
"exe",
|
||||
"dmg",
|
||||
"linux",
|
||||
"executable",
|
||||
"book",
|
||||
"font",
|
||||
"diskimage",
|
||||
"database",
|
||||
"design",
|
||||
"model3d",
|
||||
"log",
|
||||
"certificate",
|
||||
"diagram",
|
||||
"file",
|
||||
] as const;
|
||||
|
||||
@@ -94,19 +104,37 @@ export const resolveAttachmentKind = (
|
||||
) return "document";
|
||||
|
||||
if (
|
||||
mime.includes("presentation") || mime.includes("powerpoint") ||
|
||||
["ppt", "pptx", "odp", "key"].includes(extension)
|
||||
mime.includes("presentation") || mime.includes("powerpoint") || mime.includes("keynote") ||
|
||||
["ppt", "pptx", "odp", "keynote"].includes(extension) ||
|
||||
(extension === "key" && mime.includes("keynote"))
|
||||
) return "presentation";
|
||||
|
||||
if (
|
||||
mime.includes("android.package-archive") ||
|
||||
["apk", "xapk", "apks", "aab"].includes(extension)
|
||||
) return "apk";
|
||||
|
||||
if (
|
||||
mime.includes("application/x-msdownload") ||
|
||||
mime.includes("application/x-msdos-program") ||
|
||||
mime.includes("application/x-msi") ||
|
||||
["exe", "msi"].includes(extension)
|
||||
) return "exe";
|
||||
|
||||
if (
|
||||
mime.includes("application/x-apple-diskimage") ||
|
||||
["dmg", "pkg", "ipa"].includes(extension)
|
||||
) return "dmg";
|
||||
|
||||
if (
|
||||
mime.includes("application/x-debian-package") ||
|
||||
mime.includes("application/x-redhat-package-manager") ||
|
||||
["appimage", "deb", "rpm", "flatpak"].includes(extension)
|
||||
) return "linux";
|
||||
|
||||
if (
|
||||
mime.includes("application/x-executable") ||
|
||||
["apk", "xapk", "apks", "aab", "ipa", "exe", "msi", "dmg", "pkg", "deb", "rpm", "appimage"].includes(extension)
|
||||
["run", "elf"].includes(extension)
|
||||
) return "executable";
|
||||
|
||||
if (
|
||||
@@ -129,6 +157,33 @@ export const resolveAttachmentKind = (
|
||||
["sqlite", "sqlite3", "db", "db3"].includes(extension)
|
||||
) return "database";
|
||||
|
||||
if (
|
||||
mime.includes("photoshop") ||
|
||||
["psd", "psb", "ai", "sketch", "fig", "xd", "afphoto", "afdesign", "cdr"].includes(extension)
|
||||
) return "design";
|
||||
|
||||
if (
|
||||
mime.startsWith("model/") ||
|
||||
["blend", "obj", "stl", "fbx", "gltf", "glb", "step", "stp", "iges", "igs", "dwg", "dxf"].includes(extension)
|
||||
) return "model3d";
|
||||
|
||||
if (
|
||||
mime.includes("pkix") || mime.includes("x-x509") || mime.includes("pkcs") ||
|
||||
["pem", "crt", "cer", "key", "pub", "pfx", "p12", "der", "csr"].includes(extension)
|
||||
) return "certificate";
|
||||
|
||||
if (
|
||||
["xmind", "drawio", "excalidraw", "vsdx", "vsd", "mindnode", "mmap"].includes(extension)
|
||||
) return "diagram";
|
||||
|
||||
if (
|
||||
["log", "crash", "out", "trace"].includes(extension)
|
||||
) return "log";
|
||||
|
||||
if (
|
||||
["sh", "bash", "zsh", "fish", "bat", "cmd", "ps1"].includes(extension)
|
||||
) return "script";
|
||||
|
||||
if (
|
||||
mime.includes("zip") || mime.includes("compressed") || mime.includes("tar") ||
|
||||
mime.includes("rar") || mime.includes("gzip") ||
|
||||
@@ -140,11 +195,11 @@ export const resolveAttachmentKind = (
|
||||
mime.includes("xml") || mime.includes("yaml") ||
|
||||
[
|
||||
"js", "jsx", "ts", "tsx", "json", "xml", "yaml", "yml", "html", "css", "scss", "less",
|
||||
"sh", "bash", "zsh", "py", "java", "go", "rs", "c", "cpp", "h", "hpp", "cs",
|
||||
"py", "java", "go", "rs", "c", "cpp", "h", "hpp", "cs",
|
||||
"swift", "kt", "kts", "rb", "php", "lua", "sql", "toml", "ini", "conf", "env",
|
||||
].includes(extension)
|
||||
) return "code";
|
||||
|
||||
if (mime.startsWith("text/") || ["txt", "md", "log"].includes(extension)) return "text";
|
||||
if (mime.startsWith("text/") || ["txt", "md"].includes(extension)) return "text";
|
||||
return "file";
|
||||
};
|
||||
@@ -10,15 +10,88 @@ const TYPE_LABELS = {
|
||||
presentation: "PPT",
|
||||
archive: "ZIP",
|
||||
code: "CODE",
|
||||
script: "SHELL",
|
||||
text: "TXT",
|
||||
executable: "PKG",
|
||||
apk: "APK",
|
||||
exe: "EXE",
|
||||
dmg: "DMG",
|
||||
linux: "LINUX",
|
||||
executable: "BIN",
|
||||
book: "BOOK",
|
||||
font: "FONT",
|
||||
diskimage: "ISO",
|
||||
database: "DB",
|
||||
design: "DESIGN",
|
||||
model3d: "3D",
|
||||
log: "LOG",
|
||||
certificate: "KEY",
|
||||
diagram: "DIAGRAM",
|
||||
file: "FILE",
|
||||
} as const;
|
||||
|
||||
const EXTENSION_LABELS: Readonly<Record<string, string>> = {
|
||||
// Installers
|
||||
msi: "MSI",
|
||||
appimage: "APPIMAGE",
|
||||
deb: "DEB",
|
||||
rpm: "RPM",
|
||||
pkg: "PKG",
|
||||
ipa: "IPA",
|
||||
aab: "AAB",
|
||||
|
||||
// Design
|
||||
psd: "PSD",
|
||||
ai: "AI",
|
||||
sketch: "SKETCH",
|
||||
fig: "FIG",
|
||||
xd: "XD",
|
||||
|
||||
// 3D
|
||||
stl: "STL",
|
||||
obj: "OBJ",
|
||||
blend: "BLEND",
|
||||
fbx: "FBX",
|
||||
gltf: "GLTF",
|
||||
glb: "GLB",
|
||||
dwg: "DWG",
|
||||
|
||||
// Scripts
|
||||
sh: "SH",
|
||||
bash: "BASH",
|
||||
zsh: "ZSH",
|
||||
bat: "BAT",
|
||||
cmd: "CMD",
|
||||
ps1: "PS1",
|
||||
|
||||
// Certs & Keys
|
||||
pem: "PEM",
|
||||
crt: "CERT",
|
||||
cer: "CERT",
|
||||
pub: "PUB",
|
||||
key: "KEY",
|
||||
|
||||
// Diagrams
|
||||
xmind: "XMIND",
|
||||
drawio: "DRAWIO",
|
||||
vsdx: "VSDX",
|
||||
|
||||
// Books & Fonts & Media
|
||||
epub: "EPUB",
|
||||
mobi: "MOBI",
|
||||
woff2: "WOFF2",
|
||||
woff: "WOFF",
|
||||
ttf: "TTF",
|
||||
otf: "OTF",
|
||||
|
||||
// Common office/text
|
||||
csv: "CSV",
|
||||
tsv: "TSV",
|
||||
md: "MD",
|
||||
"7z": "7Z",
|
||||
tar: "TAR",
|
||||
gz: "GZ",
|
||||
};
|
||||
|
||||
export const normalizeAttachmentByteSize = (value: unknown): number | null => {
|
||||
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
|
||||
return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : null;
|
||||
@@ -36,7 +109,13 @@ export const formatAttachmentByteSize = (value: unknown): string | null => {
|
||||
export const getAttachmentTypeLabel = (
|
||||
mimeType: string | null | undefined,
|
||||
filename: string | null | undefined,
|
||||
) => TYPE_LABELS[resolveAttachmentKind(mimeType, filename)];
|
||||
) => {
|
||||
const extension = filename?.trim().toLowerCase().match(/\.([a-z0-9]+)(?:[?#].*)?$/)?.[1] ?? "";
|
||||
if (EXTENSION_LABELS[extension]) {
|
||||
return EXTENSION_LABELS[extension];
|
||||
}
|
||||
return TYPE_LABELS[resolveAttachmentKind(mimeType, filename)];
|
||||
};
|
||||
|
||||
export const formatAttachmentMetadata = (
|
||||
mimeType: string | null | undefined,
|
||||
|
||||
Reference in new issue
Block a user