diff --git a/ui/web/src/adapters/vault-graph-adapter.ts b/ui/web/src/adapters/vault-graph-adapter.ts index 6c021e83..5c033197 100644 --- a/ui/web/src/adapters/vault-graph-adapter.ts +++ b/ui/web/src/adapters/vault-graph-adapter.ts @@ -11,6 +11,7 @@ export const VAULT_TYPE_COLORS: Record = { skill: "#22c55e", // green episodic: "#f59e0b", // amber media: "#ec4899", // pink (dominant, soft) + document: "#06b6d4", // cyan (Phase 01: PDF/office — distinct from media) }; const DEFAULT_COLOR = "#9ca3af"; diff --git a/ui/web/src/i18n/locales/en/vault.json b/ui/web/src/i18n/locales/en/vault.json index 94a0dbd5..19fee6a8 100644 --- a/ui/web/src/i18n/locales/en/vault.json +++ b/ui/web/src/i18n/locales/en/vault.json @@ -65,7 +65,8 @@ "note": "Note", "skill": "Skill", "episodic": "Episodic", - "media": "Media" + "media": "Media", + "document": "Document" }, "rescanTooltip": "Rescan workspace", "enriching": "Enriching", diff --git a/ui/web/src/i18n/locales/vi/vault.json b/ui/web/src/i18n/locales/vi/vault.json index d17ef7ee..4b360a32 100644 --- a/ui/web/src/i18n/locales/vi/vault.json +++ b/ui/web/src/i18n/locales/vi/vault.json @@ -65,7 +65,8 @@ "note": "Ghi chú", "skill": "Kỹ năng", "episodic": "Giai thoại", - "media": "Media" + "media": "Media", + "document": "Tài liệu" }, "rescanTooltip": "Quét lại workspace", "enriching": "Đang xử lý", diff --git a/ui/web/src/i18n/locales/zh/vault.json b/ui/web/src/i18n/locales/zh/vault.json index bf51031f..346730e4 100644 --- a/ui/web/src/i18n/locales/zh/vault.json +++ b/ui/web/src/i18n/locales/zh/vault.json @@ -65,7 +65,8 @@ "note": "笔记", "skill": "技能", "episodic": "情景记忆", - "media": "Media" + "media": "Media", + "document": "文档" }, "rescanTooltip": "重新扫描工作区", "enriching": "处理中", diff --git a/ui/web/src/pages/vault/vault-detail-dialog.tsx b/ui/web/src/pages/vault/vault-detail-dialog.tsx index a5adfbcf..ea7c6009 100644 --- a/ui/web/src/pages/vault/vault-detail-dialog.tsx +++ b/ui/web/src/pages/vault/vault-detail-dialog.tsx @@ -43,10 +43,14 @@ export function VaultDetailDialog({ doc, open, onOpenChange, onDeleted }: Props) }, [doc?.metadata, doc?.path]); const isMedia = doc?.doc_type === "media"; + // Phase 01: `document` docType covers PDFs / office docs — also binary, so + // avoid the text-content fetch but still re-use the media image pathway + // only for true media (images stay rendered, documents show a placeholder). + const isBinary = isMedia || doc?.doc_type === "document"; - // Only fetch text content for non-media files (media/binary cannot be rendered as markdown). + // Only fetch text content for non-binary files (media/document are binary and cannot be rendered as markdown). const { content: fileContent, loading: contentLoading, error: contentError } = useVaultFileContent( - open && doc && !isMedia ? doc.path : null, + open && doc && !isBinary ? doc.path : null, ); // Fetch image as authenticated blob URL for rendering. const { url: imageUrl, error: imageError } = useVaultImageUrl(open && isMedia && isImage && doc ? doc.path : null); @@ -161,8 +165,8 @@ export function VaultDetailDialog({ doc, open, onOpenChange, onDeleted }: Props) {/* Content preview — always visible, scrollable */}
- {isMedia ? ( - isImage ? ( + {isBinary ? ( + isMedia && isImage ? (
{imageUrl ? ( = { skill: "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300", episodic: "bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300", media: "bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300", + document: "bg-cyan-100 text-cyan-700 dark:bg-cyan-900 dark:text-cyan-300", }; const SCOPE_COLORS: Record = { diff --git a/ui/web/src/types/vault.ts b/ui/web/src/types/vault.ts index 9ea805f5..416118d3 100644 --- a/ui/web/src/types/vault.ts +++ b/ui/web/src/types/vault.ts @@ -7,8 +7,21 @@ export interface VaultDocument { scope: "personal" | "team" | "shared"; custom_scope?: string; path: string; + /** Lowercased basename (`/absolute/path/foo.md` → `foo.md`). PG-generated / SQLite app-populated. */ + path_basename?: string; title: string; - doc_type: "context" | "memory" | "note" | "skill" | "episodic" | "media"; + /** + * - `document` added in Phase 01 for PDFs + office files (separate from `media` + * so enrichment can apply idempotent deterministic summaries). + */ + doc_type: + | "context" + | "memory" + | "note" + | "skill" + | "episodic" + | "media" + | "document"; content_hash: string; summary?: string; metadata: Record | null; @@ -16,13 +29,21 @@ export interface VaultDocument { updated_at: string; } -/** Directed link between two vault documents (wikilinks). */ +/** Directed link between two vault documents (wikilinks + auto-linking). */ export interface VaultLink { id: string; from_doc_id: string; to_doc_id: string; + /** + * - Classify types: `reference`, `related`, `extends`, `depends_on`, `supersedes`, `contradicts`. + * - Wiki: `wikilink`. + * - Phase 04/05 auto-linking: `task_attachment`, `delegation_attachment` + * (invisible to classify wipe — never user-creatable via the link dialog). + */ link_type: string; context: string; + /** Cleanup tracking metadata, e.g. `{ "source": "task:{uuid}" }` for auto-links. */ + metadata?: Record | null; created_at: string; }