1import { ItemView, Notice, WorkspaceLeaf } from "obsidian";2import type SublimatedPlugin from "./main";3import { SublimatedApi, type Version } from "./api";4
5export const VERSION_VIEW_TYPE = "sublimated-versions";6
7export class VersionView extends ItemView {8 plugin: SublimatedPlugin;9
10 constructor(leaf: WorkspaceLeaf, plugin: SublimatedPlugin) {11 super(leaf);12 this.plugin = plugin;13 }14
15 getViewType(): string {16 return VERSION_VIEW_TYPE;17 }18
19 getDisplayText(): string {20 return "sublimated versions";21 }22
23 getIcon(): string {24 return "history";25 }26
27 async onOpen(): Promise<void> {28 await this.refresh();29 // refresh when active file changes30 this.registerEvent(31 this.app.workspace.on("active-leaf-change", () => {32 this.refresh();33 })34 );35 }36
37 async refresh(): Promise<void> {38 const { contentEl } = this;39 contentEl.empty();40 contentEl.addClass("sbl-version-view");41
42 const activeFile = this.app.workspace.getActiveFile();43 if (!activeFile) {44 contentEl.createEl("p", {45 text: "No file open.",46 cls: "sbl-version-empty",47 });48 return;49 }50
51 if (!this.plugin.settings.apiToken) {52 contentEl.createEl("p", {53 text: "API token required for version history.",54 cls: "sbl-version-empty",55 });56 contentEl.createEl("p", {57 text: "Configure in Settings → sublimated",58 cls: "sbl-version-hint",59 });60 return;61 }62
63 // read xattrs to get space + item ID64 const meta = this.plugin.lastMetadata;65 if (!meta?.space || !meta?.id) {66 contentEl.createEl("p", {67 text: "Not a sublimated file.",68 cls: "sbl-version-empty",69 });70 return;71 }72
73 contentEl.createEl("h4", {74 text: activeFile.name,75 cls: "sbl-version-filename",76 });77
78 const loading = contentEl.createEl("p", {79 text: "Loading versions...",80 cls: "sbl-version-loading",81 });82
83 try {84 const api = new SublimatedApi(this.plugin.settings.apiToken);85 const versions = await api.getVersions(meta.space, meta.id);86 loading.remove();87
88 if (versions.length === 0) {89 contentEl.createEl("p", {90 text: "No versions found.",91 cls: "sbl-version-empty",92 });93 return;94 }95
96 const list = contentEl.createEl("div", {97 cls: "sbl-version-list",98 });99 for (const v of versions) {100 this.renderVersion(list, v, meta.url);101 }102 } catch (e) {103 loading.remove();104 contentEl.createEl("p", {105 text: `Error: ${e instanceof Error ? e.message : "Unknown"}`,106 cls: "sbl-version-error",107 });108 }109 }110
111 private renderVersion(112 container: HTMLElement,113 v: Version,114 fileUrl: string | null115 ): void {116 const item = container.createDiv({ cls: "sbl-version-item" });117
118 const header = item.createDiv({ cls: "sbl-version-header" });119 header.createEl("span", {120 text: `v${v.number}`,121 cls: "sbl-version-number",122 });123 header.createEl("span", {124 text: v.authorName,125 cls: "sbl-version-author",126 });127
128 const time = item.createDiv({ cls: "sbl-version-time" });129 time.createEl("span", {130 text: formatRelativeTime(v.createdAt),131 attr: { title: new Date(v.createdAt).toLocaleString() },132 });133
134 if (v.message) {135 item.createDiv({136 text: v.message,137 cls: "sbl-version-message",138 });139 }140
141 if (fileUrl) {142 const webUrl = `https://${fileUrl}?v=${v.number}`;143 const link = item.createEl("a", {144 text: "view on web",145 cls: "sbl-version-web-link",146 href: webUrl,147 });148 link.addEventListener("click", (e) => {149 e.preventDefault();150 window.open(webUrl);151 });152 }153 }154
155 async onClose(): Promise<void> {156 // cleanup handled by obsidian157 }158}159
160function formatRelativeTime(iso: string): string {161 const now = Date.now();162 const then = new Date(iso).getTime();163 const diff = now - then;164 const seconds = Math.floor(diff / 1000);165 const minutes = Math.floor(seconds / 60);166 const hours = Math.floor(minutes / 60);167 const days = Math.floor(hours / 24);168
169 if (days > 30) return new Date(iso).toLocaleDateString();170 if (days > 0) return `${days}d ago`;171 if (hours > 0) return `${hours}h ago`;172 if (minutes > 0) return `${minutes}m ago`;173 return "just now";174} v1 of v8
commit 9755c30
message create version-view.ts via macOS
author anton
date Apr 8, 2026
size 4.0 KB