A /sublimated/undefined +174 -0
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}A /sublimated/undefined +69 -0
1import { requestUrl } from "obsidian";2
3const API_BASE = "https://api.sublimated.com";4
5export interface Version {6 id: string;7 number: number;8 authorName: string;9 authorType: string;10 message: string | null;11 createdAt: string;12 commitHash: string | null;13}14
15export interface VersionsResponse {16 versions: Version[];17}18
19export interface TestConnectionResult {20 ok: boolean;21 username?: string;22 error?: string;23}24
25export class SublimatedApi {26 constructor(private token: string) {}27
28 private async request<T>(path: string): Promise<T> {29 const response = await requestUrl({30 url: `${API_BASE}${path}`,31 headers: {32 Authorization: `Bearer ${this.token}`,33 Accept: "application/json",34 },35 });36
37 if (response.status >= 400) {38 throw new Error(39 `API error ${response.status}: ${response.text}`40 );41 }42
43 return response.json as T;44 }45
46 async getVersions(47 spaceSlug: string,48 itemId: string49 ): Promise<Version[]> {50 const data = await this.request<VersionsResponse>(51 `/v1/spaces/${encodeURIComponent(spaceSlug)}/items/${encodeURIComponent(itemId)}/versions`52 );53 return data.versions;54 }55
56 async testConnection(): Promise<TestConnectionResult> {57 try {58 const data = await this.request<{ username: string }>(59 "/v1/me"60 );61 return { ok: true, username: data.username };62 } catch (e) {63 return {64 ok: false,65 error: e instanceof Error ? e.message : "Unknown error",66 };67 }68 }69}A /sublimated/undefined +39 -0
1import esbuild from "esbuild";2import builtins from "builtin-modules";3import process from "process";4
5const watch = process.argv.includes("--watch");6
7const ctx = await esbuild.context({8 entryPoints: ["src/main.ts"],9 bundle: true,10 external: [11 "obsidian",12 "electron",13 "@codemirror/autocomplete",14 "@codemirror/collab",15 "@codemirror/commands",16 "@codemirror/language",17 "@codemirror/lint",18 "@codemirror/search",19 "@codemirror/state",20 "@codemirror/view",21 "@lezer/common",22 "@lezer/highlight",23 "@lezer/lr",24 ...builtins,25 ],26 format: "cjs",27 target: "es2022",28 logLevel: "info",29 sourcemap: "inline",30 treeShaking: true,31 outfile: "main.js",32});33
34if (watch) {35 await ctx.watch();36} else {37 await ctx.rebuild();38 process.exit(0);39}A /sublimated/undefined +17 -0
1{2 "compilerOptions": {3 "baseUrl": ".",4 "inlineSourceMap": true,5 "inlineSources": true,6 "module": "ESNext",7 "target": "ES2022",8 "allowJs": true,9 "noImplicitAny": true,10 "moduleResolution": "bundler",11 "importHelpers": true,12 "isolatedModules": true,13 "strictNullChecks": true,14 "lib": ["ES2022", "DOM", "DOM.Iterable"]15 },16 "include": ["src/**/*.ts"]17}A /sublimated/undefined +17 -0
1{2 "name": "obsidian-sublimated",3 "version": "0.1.0",4 "private": true,5 "description": "Obsidian plugin for sublimated.com",6 "scripts": {7 "build": "node esbuild.config.mjs",8 "dev": "node esbuild.config.mjs --watch"9 },10 "devDependencies": {11 "@types/node": "^22.0.0",12 "builtin-modules": "^4.0.0",13 "esbuild": "^0.25.0",14 "obsidian": "^1.7.2",15 "typescript": "^5.7.0"16 }17}A /sublimated/undefined +115 -0
1import { App, Notice, PluginSettingTab, Setting } from "obsidian";2import type SublimatedPlugin from "./main";3import { SublimatedApi } from "./api";4
5export interface SublimatedSettings {6 apiToken: string;7 hasSeenWelcome: boolean;8}9
10export const DEFAULT_SETTINGS: SublimatedSettings = {11 apiToken: "",12 hasSeenWelcome: false,13};14
15export class SublimatedSettingTab extends PluginSettingTab {16 plugin: SublimatedPlugin;17
18 constructor(app: App, plugin: SublimatedPlugin) {19 super(app, plugin);20 this.plugin = plugin;21 }22
23 display(): void {24 const { containerEl } = this;25 containerEl.empty();26
27 // branding28 const header = containerEl.createDiv({ cls: "sbl-settings-header" });29 header.createEl("h2", { text: "sublimated" });30 header.createEl("p", {31 text: "Sync status, version history, and deep links.",32 cls: "setting-item-description",33 });34
35 // workspace detection36 const workspaceInfo = this.plugin.detectWorkspace();37 if (workspaceInfo) {38 new Setting(containerEl)39 .setName("Detected workspace")40 .setDesc(41 `Space: ${workspaceInfo.space || "unknown"} — .sbl/ folder found in vault root`42 );43 } else {44 new Setting(containerEl)45 .setName("No workspace detected")46 .setDesc(47 "No .sbl/ folder found. Open a vault from your sublimated folder in Finder."48 );49 }50
51 // API token52 new Setting(containerEl)53 .setName("API token")54 .setDesc(55 "Required for version history. Generate at sublimated.com/settings/tokens"56 )57 .addText((text) =>58 text59 .setPlaceholder("sk_...")60 .setValue(this.plugin.settings.apiToken)61 .onChange(async (value) => {62 this.plugin.settings.apiToken = value;63 await this.plugin.saveSettings();64 })65 );66
67 // test connection68 new Setting(containerEl)69 .setName("Test connection")70 .setDesc("Verify your API token works")71 .addButton((btn) =>72 btn.setButtonText("Test").onClick(async () => {73 if (!this.plugin.settings.apiToken) {74 new Notice("No API token configured");75 return;76 }77 btn.setButtonText("Testing...");78 btn.setDisabled(true);79 const api = new SublimatedApi(80 this.plugin.settings.apiToken81 );82 const result = await api.testConnection();83 if (result.ok) {84 new Notice(85 `Connected as ${result.username}`86 );87 } else {88 new Notice(89 `Connection failed: ${result.error}`90 );91 }92 btn.setButtonText("Test");93 btn.setDisabled(false);94 })95 );96
97 // links98 containerEl.createEl("div", { cls: "sbl-settings-links" }, (el) => {99 el.createEl("a", {100 text: "sublimated.com",101 href: "https://sublimated.com",102 });103 el.createEl("span", { text: " · " });104 el.createEl("a", {105 text: "Documentation",106 href: "https://sublimated.com/docs/obsidian",107 });108 el.createEl("span", { text: " · " });109 el.createEl("a", {110 text: "Source code",111 href: "https://sublimated.com/sublimated/code/obsidian",112 });113 });114 }115}M /sublimated/undefined
binary file or content unavailable
A /sublimated/undefined +244 -0
1import {2 Notice,3 Plugin,4 WorkspaceLeaf,5 normalizePath,6} from "obsidian";7import {8 type SublimatedSettings,9 DEFAULT_SETTINGS,10 SublimatedSettingTab,11} from "./settings";12import { type FileMetadata, readFileMetadata, isMacOS } from "./xattr";13import { VersionView, VERSION_VIEW_TYPE } from "./version-view";14import { WelcomeModal } from "./welcome-modal";15
16export default class SublimatedPlugin extends Plugin {17 settings: SublimatedSettings = DEFAULT_SETTINGS;18 lastMetadata: FileMetadata | null = null;19
20 private statusBarEl: HTMLElement | null = null;21 private isSublimatedVault = false;22
23 async onload(): Promise<void> {24 await this.loadSettings();25
26 // detect if this vault is a sublimated workspace27 this.isSublimatedVault = await this.checkSublimatedVault();28
29 // status bar30 this.statusBarEl = this.addStatusBarItem();31 this.statusBarEl.addClass("sbl-status-bar");32 this.statusBarEl.addEventListener("click", () =>33 this.openCurrentFileOnWeb()34 );35 this.updateStatusBar(null);36
37 // register version history view38 this.registerView(39 VERSION_VIEW_TYPE,40 (leaf) => new VersionView(leaf, this)41 );42
43 // commands44 this.addCommand({45 id: "open-on-web",46 name: "Open on sublimated.com",47 callback: () => this.openCurrentFileOnWeb(),48 });49
50 this.addCommand({51 id: "show-versions",52 name: "Show versions",53 callback: () => this.activateVersionView(),54 });55
56 this.addCommand({57 id: "copy-web-link",58 name: "Copy web link",59 callback: () => this.copyWebLink(),60 });61
62 // ribbon icon for version sidebar63 this.addRibbonIcon("history", "sublimated versions", () => {64 this.activateVersionView();65 });66
67 // URI handler: obsidian://sublimated?action=open&file=path/to/note.md68 this.registerObsidianProtocolHandler(69 "sublimated",70 (params) => this.handleUri(params)71 );72
73 // settings tab74 this.addSettingTab(new SublimatedSettingTab(this.app, this));75
76 // update status bar when active file changes77 this.registerEvent(78 this.app.workspace.on("active-leaf-change", () => {79 this.refreshMetadata();80 })81 );82
83 // initial metadata read84 this.refreshMetadata();85
86 // welcome modal on first install87 if (!this.settings.hasSeenWelcome) {88 new WelcomeModal(this.app).open();89 this.settings.hasSeenWelcome = true;90 await this.saveSettings();91 }92 }93
94 onunload(): void {95 this.app.workspace.detachLeavesOfType(VERSION_VIEW_TYPE);96 }97
98 async loadSettings(): Promise<void> {99 this.settings = Object.assign(100 {},101 DEFAULT_SETTINGS,102 await this.loadData()103 );104 }105
106 async saveSettings(): Promise<void> {107 await this.saveData(this.settings);108 }109
110 // check for .sbl/ folder in vault root111 private async checkSublimatedVault(): Promise<boolean> {112 const adapter = this.app.vault.adapter;113 return adapter.exists(normalizePath(".sbl"));114 }115
116 detectWorkspace(): { space: string | null } | null {117 if (!this.isSublimatedVault) return null;118 return {119 space: this.lastMetadata?.space ?? null,120 };121 }122
123 private async refreshMetadata(): Promise<void> {124 const file = this.app.workspace.getActiveFile();125 if (!file) {126 this.lastMetadata = null;127 this.updateStatusBar(null);128 return;129 }130
131 if (!isMacOS()) {132 this.lastMetadata = null;133 this.updateStatusBar(null);134 return;135 }136
137 // get absolute path via vault adapter138 const adapter = this.app.vault.adapter;139 const basePath = (adapter as { getBasePath?: () => string })140 .getBasePath?.();141 if (!basePath) {142 this.lastMetadata = null;143 this.updateStatusBar(null);144 return;145 }146
147 const absPath = `${basePath}/${file.path}`;148 this.lastMetadata = await readFileMetadata(absPath);149 this.updateStatusBar(this.lastMetadata);150 }151
152 private updateStatusBar(meta: FileMetadata | null): void {153 if (!this.statusBarEl) return;154
155 if (!this.isSublimatedVault) {156 this.statusBarEl.setText("");157 return;158 }159
160 if (!meta) {161 this.statusBarEl.setText("sublimated");162 return;163 }164
165 const parts: string[] = [];166 if (meta.version) parts.push(`v${meta.version}`);167 if (meta.syncStatus) parts.push(meta.syncStatus);168 if (meta.modifiedBy) parts.push(`by ${meta.modifiedBy}`);169
170 if (parts.length > 0) {171 this.statusBarEl.setText(`sublimated: ${parts.join(" · ")}`);172 } else {173 this.statusBarEl.setText("sublimated");174 }175
176 // show pointer cursor when URL available177 if (meta.url) {178 this.statusBarEl.addClass("sbl-clickable");179 } else {180 this.statusBarEl.removeClass("sbl-clickable");181 }182 }183
184 private openCurrentFileOnWeb(): void {185 const url = this.lastMetadata?.url;186 if (!url) {187 new Notice("No sublimated URL for this file");188 return;189 }190 window.open(`https://${url}`);191 }192
193 private async copyWebLink(): Promise<void> {194 const url = this.lastMetadata?.url;195 if (!url) {196 new Notice("No sublimated URL for this file");197 return;198 }199 await navigator.clipboard.writeText(`https://${url}`);200 new Notice("Link copied to clipboard");201 }202
203 private async activateVersionView(): Promise<void> {204 const existing =205 this.app.workspace.getLeavesOfType(VERSION_VIEW_TYPE);206 if (existing.length > 0) {207 this.app.workspace.revealLeaf(existing[0]);208 return;209 }210
211 const leaf = this.app.workspace.getRightLeaf(false);212 if (!leaf) return;213 await leaf.setViewState({214 type: VERSION_VIEW_TYPE,215 active: true,216 });217 this.app.workspace.revealLeaf(leaf);218 }219
220 private handleUri(params: Record<string, string>): void {221 const { action, file } = params;222
223 if (action === "open" && file) {224 const normalized = normalizePath(file);225 const existing = this.app.vault.getAbstractFileByPath(normalized);226 if (existing) {227 this.app.workspace.openLinkText(normalized, "", false);228 } else {229 new Notice(`File not found: ${file}`);230 }231 return;232 }233
234 if (action === "auth" && params.token) {235 // future: OAuth flow stores token from redirect236 this.settings.apiToken = params.token;237 this.saveSettings();238 new Notice("sublimated: signed in successfully");239 return;240 }241
242 new Notice(`Unknown sublimated action: ${action}`);243 }244}A /sublimated/undefined +59 -0
1import { App, Modal } from "obsidian";2
3export class WelcomeModal extends Modal {4 constructor(app: App) {5 super(app);6 }7
8 onOpen(): void {9 const { contentEl } = this;10 contentEl.addClass("sbl-welcome-modal");11
12 contentEl.createEl("h2", { text: "Welcome to sublimated" });13
14 contentEl.createEl("p", {15 text: "sublimated syncs your files with version history, collaboration, and a web viewer — all powered by git under the hood.",16 });17
18 const steps = contentEl.createEl("div", { cls: "sbl-welcome-steps" });19
20 const step1 = steps.createDiv({ cls: "sbl-welcome-step" });21 step1.createEl("strong", { text: "1. Open your vault from sublimated" });22 step1.createEl("p", {23 text: "In Finder, open your sublimated folder (under CloudStorage) as an Obsidian vault. The plugin detects the .sbl/ folder automatically.",24 });25
26 const step2 = steps.createDiv({ cls: "sbl-welcome-step" });27 step2.createEl("strong", { text: "2. Status bar shows sync info" });28 step2.createEl("p", {29 text: "The bottom bar shows version number, sync status, and author for the current file — all read from local file metadata, no account needed.",30 });31
32 const step3 = steps.createDiv({ cls: "sbl-welcome-step" });33 step3.createEl("strong", { text: "3. Add an API token for version history" });34 step3.createEl("p", {35 text: "To browse past versions, add your API token in Settings → sublimated. Generate one at sublimated.com/settings/tokens.",36 });37
38 const links = contentEl.createDiv({ cls: "sbl-welcome-links" });39 links.createEl("a", {40 text: "Learn more at sublimated.com",41 href: "https://sublimated.com",42 });43 links.createEl("br");44 links.createEl("a", {45 text: "Documentation",46 href: "https://sublimated.com/docs/obsidian",47 });48
49 const footer = contentEl.createDiv({ cls: "sbl-welcome-footer" });50 footer.createEl("button", {51 text: "Get started",52 cls: "mod-cta",53 }).addEventListener("click", () => this.close());54 }55
56 onClose(): void {57 this.contentEl.empty();58 }59}A /sublimated/undefined +84 -0
1import { exec } from "child_process";2import { Platform } from "obsidian";3
4const XATTR_PREFIX = "com.sublimated.";5const XATTR_SUFFIX = "#PS";6
7export interface FileMetadata {8 version: string | null;9 commit: string | null;10 url: string | null;11 contentHash: string | null;12 id: string | null;13 createdBy: string | null;14 modifiedBy: string | null;15 modifiedByType: string | null;16 syncStatus: string | null;17 space: string | null;18 branch: string | null;19 modifiedAt: string | null;20}21
22const ATTRS: (keyof FileMetadata)[] = [23 "version",24 "commit",25 "url",26 "contentHash",27 "id",28 "createdBy",29 "modifiedBy",30 "modifiedByType",31 "syncStatus",32 "space",33 "branch",34 "modifiedAt",35];36
37function attrToXattrName(attr: string): string {38 // camelCase → kebab-case: contentHash → content-hash39 const kebab = attr.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase());40 return `${XATTR_PREFIX}${kebab}${XATTR_SUFFIX}`;41}42
43function readXattr(path: string, xattrName: string): Promise<string | null> {44 return new Promise((resolve) => {45 exec(46 `xattr -p ${JSON.stringify(xattrName)} ${JSON.stringify(path)}`,47 { timeout: 2000 },48 (err, stdout) => {49 if (err) {50 resolve(null);51 return;52 }53 resolve(stdout.trim() || null);54 }55 );56 });57}58
59export async function readFileMetadata(60 path: string61): Promise<FileMetadata | null> {62 if (!Platform.isMacOS) return null;63
64 const results = await Promise.all(65 ATTRS.map((attr) => readXattr(path, attrToXattrName(attr)))66 );67
68 // If no xattrs found at all, this isn't a sublimated file69 if (results.every((r) => r === null)) return null;70
71 const meta: FileMetadata = {72 version: null, commit: null, url: null, contentHash: null,73 id: null, createdBy: null, modifiedBy: null, modifiedByType: null,74 syncStatus: null, space: null, branch: null, modifiedAt: null,75 };76 ATTRS.forEach((attr, i) => {77 meta[attr] = results[i];78 });79 return meta;80}81
82export function isMacOS(): boolean {83 return Platform.isMacOS;84}A /sublimated/undefined +594 -0
1var __defProp = Object.defineProperty;2var __getOwnPropDesc = Object.getOwnPropertyDescriptor;3var __getOwnPropNames = Object.getOwnPropertyNames;4var __hasOwnProp = Object.prototype.hasOwnProperty;5var __export = (target, all) => {6 for (var name in all)7 __defProp(target, name, { get: all[name], enumerable: true });8};9var __copyProps = (to, from, except, desc) => {10 if (from && typeof from === "object" || typeof from === "function") {11 for (let key of __getOwnPropNames(from))12 if (!__hasOwnProp.call(to, key) && key !== except)13 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });14 }15 return to;16};17var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);18
19// src/main.ts20var main_exports = {};21__export(main_exports, {22 default: () => SublimatedPlugin23});24module.exports = __toCommonJS(main_exports);25var import_obsidian6 = require("obsidian");26
27// src/settings.ts28var import_obsidian2 = require("obsidian");29
30// src/api.ts31var import_obsidian = require("obsidian");32var API_BASE = "https://api.sublimated.com";33var SublimatedApi = class {34 constructor(token) {35 this.token = token;36 }37 async request(path) {38 const response = await (0, import_obsidian.requestUrl)({39 url: `${API_BASE}${path}`,40 headers: {41 Authorization: `Bearer ${this.token}`,42 Accept: "application/json"43 }44 });45 if (response.status >= 400) {46 throw new Error(47 `API error ${response.status}: ${response.text}`48 );49 }50 return response.json;51 }52 async getVersions(spaceSlug, itemId) {53 const data = await this.request(54 `/v1/spaces/${encodeURIComponent(spaceSlug)}/items/${encodeURIComponent(itemId)}/versions`55 );56 return data.versions;57 }58 async testConnection() {59 try {60 const data = await this.request(61 "/v1/me"62 );63 return { ok: true, username: data.username };64 } catch (e) {65 return {66 ok: false,67 error: e instanceof Error ? e.message : "Unknown error"68 };69 }70 }71};72
73// src/settings.ts74var DEFAULT_SETTINGS = {75 apiToken: "",76 hasSeenWelcome: false77};78var SublimatedSettingTab = class extends import_obsidian2.PluginSettingTab {79 plugin;80 constructor(app, plugin) {81 super(app, plugin);82 this.plugin = plugin;83 }84 display() {85 const { containerEl } = this;86 containerEl.empty();87 const header = containerEl.createDiv({ cls: "sbl-settings-header" });88 header.createEl("h2", { text: "sublimated" });89 header.createEl("p", {90 text: "Sync status, version history, and deep links.",91 cls: "setting-item-description"92 });93 const workspaceInfo = this.plugin.detectWorkspace();94 if (workspaceInfo) {95 new import_obsidian2.Setting(containerEl).setName("Detected workspace").setDesc(96 `Space: ${workspaceInfo.space || "unknown"} \u2014 .sbl/ folder found in vault root`97 );98 } else {99 new import_obsidian2.Setting(containerEl).setName("No workspace detected").setDesc(100 "No .sbl/ folder found. Open a vault from your sublimated folder in Finder."101 );102 }103 new import_obsidian2.Setting(containerEl).setName("API token").setDesc(104 "Required for version history. Generate at sublimated.com/settings/tokens"105 ).addText(106 (text) => text.setPlaceholder("sk_...").setValue(this.plugin.settings.apiToken).onChange(async (value) => {107 this.plugin.settings.apiToken = value;108 await this.plugin.saveSettings();109 })110 );111 new import_obsidian2.Setting(containerEl).setName("Test connection").setDesc("Verify your API token works").addButton(112 (btn) => btn.setButtonText("Test").onClick(async () => {113 if (!this.plugin.settings.apiToken) {114 new import_obsidian2.Notice("No API token configured");115 return;116 }117 btn.setButtonText("Testing...");118 btn.setDisabled(true);119 const api = new SublimatedApi(120 this.plugin.settings.apiToken121 );122 const result = await api.testConnection();123 if (result.ok) {124 new import_obsidian2.Notice(125 `Connected as ${result.username}`126 );127 } else {128 new import_obsidian2.Notice(129 `Connection failed: ${result.error}`130 );131 }132 btn.setButtonText("Test");133 btn.setDisabled(false);134 })135 );136 containerEl.createEl("div", { cls: "sbl-settings-links" }, (el) => {137 el.createEl("a", {138 text: "sublimated.com",139 href: "https://sublimated.com"140 });141 el.createEl("span", { text: " \xB7 " });142 el.createEl("a", {143 text: "Documentation",144 href: "https://sublimated.com/docs/obsidian"145 });146 el.createEl("span", { text: " \xB7 " });147 el.createEl("a", {148 text: "Report an issue",149 href: "https://github.com/nichochar/obsidian-sublimated/issues"150 });151 });152 }153};154
155// src/xattr.ts156var import_child_process = require("child_process");157var import_obsidian3 = require("obsidian");158var XATTR_PREFIX = "com.sublimated.";159var XATTR_SUFFIX = "#PS";160var ATTRS = [161 "version",162 "commit",163 "url",164 "contentHash",165 "id",166 "createdBy",167 "modifiedBy",168 "modifiedByType",169 "syncStatus",170 "space",171 "branch",172 "modifiedAt"173];174function attrToXattrName(attr) {175 const kebab = attr.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase());176 return `${XATTR_PREFIX}${kebab}${XATTR_SUFFIX}`;177}178function readXattr(path, xattrName) {179 return new Promise((resolve) => {180 (0, import_child_process.exec)(181 `xattr -p ${JSON.stringify(xattrName)} ${JSON.stringify(path)}`,182 { timeout: 2e3 },183 (err, stdout) => {184 if (err) {185 resolve(null);186 return;187 }188 resolve(stdout.trim() || null);189 }190 );191 });192}193async function readFileMetadata(path) {194 if (!import_obsidian3.Platform.isMacOS) return null;195 const results = await Promise.all(196 ATTRS.map((attr) => readXattr(path, attrToXattrName(attr)))197 );198 if (results.every((r) => r === null)) return null;199 const meta = {200 version: null,201 commit: null,202 url: null,203 contentHash: null,204 id: null,205 createdBy: null,206 modifiedBy: null,207 modifiedByType: null,208 syncStatus: null,209 space: null,210 branch: null,211 modifiedAt: null212 };213 ATTRS.forEach((attr, i) => {214 meta[attr] = results[i];215 });216 return meta;217}218function isMacOS() {219 return import_obsidian3.Platform.isMacOS;220}221
222// src/version-view.ts223var import_obsidian4 = require("obsidian");224var VERSION_VIEW_TYPE = "sublimated-versions";225var VersionView = class extends import_obsidian4.ItemView {226 plugin;227 constructor(leaf, plugin) {228 super(leaf);229 this.plugin = plugin;230 }231 getViewType() {232 return VERSION_VIEW_TYPE;233 }234 getDisplayText() {235 return "sublimated versions";236 }237 getIcon() {238 return "history";239 }240 async onOpen() {241 await this.refresh();242 this.registerEvent(243 this.app.workspace.on("active-leaf-change", () => {244 this.refresh();245 })246 );247 }248 async refresh() {249 const { contentEl } = this;250 contentEl.empty();251 contentEl.addClass("sbl-version-view");252 const activeFile = this.app.workspace.getActiveFile();253 if (!activeFile) {254 contentEl.createEl("p", {255 text: "No file open.",256 cls: "sbl-version-empty"257 });258 return;259 }260 if (!this.plugin.settings.apiToken) {261 contentEl.createEl("p", {262 text: "API token required for version history.",263 cls: "sbl-version-empty"264 });265 contentEl.createEl("p", {266 text: "Configure in Settings \u2192 sublimated",267 cls: "sbl-version-hint"268 });269 return;270 }271 const meta = this.plugin.lastMetadata;272 if (!meta?.space || !meta?.id) {273 contentEl.createEl("p", {274 text: "Not a sublimated file.",275 cls: "sbl-version-empty"276 });277 return;278 }279 contentEl.createEl("h4", {280 text: activeFile.name,281 cls: "sbl-version-filename"282 });283 const loading = contentEl.createEl("p", {284 text: "Loading versions...",285 cls: "sbl-version-loading"286 });287 try {288 const api = new SublimatedApi(this.plugin.settings.apiToken);289 const versions = await api.getVersions(meta.space, meta.id);290 loading.remove();291 if (versions.length === 0) {292 contentEl.createEl("p", {293 text: "No versions found.",294 cls: "sbl-version-empty"295 });296 return;297 }298 const list = contentEl.createEl("div", {299 cls: "sbl-version-list"300 });301 for (const v of versions) {302 this.renderVersion(list, v, meta.url);303 }304 } catch (e) {305 loading.remove();306 contentEl.createEl("p", {307 text: `Error: ${e instanceof Error ? e.message : "Unknown"}`,308 cls: "sbl-version-error"309 });310 }311 }312 renderVersion(container, v, fileUrl) {313 const item = container.createDiv({ cls: "sbl-version-item" });314 const header = item.createDiv({ cls: "sbl-version-header" });315 header.createEl("span", {316 text: `v${v.number}`,317 cls: "sbl-version-number"318 });319 header.createEl("span", {320 text: v.authorName,321 cls: "sbl-version-author"322 });323 const time = item.createDiv({ cls: "sbl-version-time" });324 time.createEl("span", {325 text: formatRelativeTime(v.createdAt),326 attr: { title: new Date(v.createdAt).toLocaleString() }327 });328 if (v.message) {329 item.createDiv({330 text: v.message,331 cls: "sbl-version-message"332 });333 }334 if (fileUrl) {335 const webUrl = `https://${fileUrl}?v=${v.number}`;336 const link = item.createEl("a", {337 text: "view on web",338 cls: "sbl-version-web-link",339 href: webUrl340 });341 link.addEventListener("click", (e) => {342 e.preventDefault();343 window.open(webUrl);344 });345 }346 }347 async onClose() {348 }349};350function formatRelativeTime(iso) {351 const now = Date.now();352 const then = new Date(iso).getTime();353 const diff = now - then;354 const seconds = Math.floor(diff / 1e3);355 const minutes = Math.floor(seconds / 60);356 const hours = Math.floor(minutes / 60);357 const days = Math.floor(hours / 24);358 if (days > 30) return new Date(iso).toLocaleDateString();359 if (days > 0) return `${days}d ago`;360 if (hours > 0) return `${hours}h ago`;361 if (minutes > 0) return `${minutes}m ago`;362 return "just now";363}364
365// src/welcome-modal.ts366var import_obsidian5 = require("obsidian");367var WelcomeModal = class extends import_obsidian5.Modal {368 constructor(app) {369 super(app);370 }371 onOpen() {372 const { contentEl } = this;373 contentEl.addClass("sbl-welcome-modal");374 contentEl.createEl("h2", { text: "Welcome to sublimated" });375 contentEl.createEl("p", {376 text: "sublimated syncs your files with version history, collaboration, and a web viewer \u2014 all powered by git under the hood."377 });378 const steps = contentEl.createEl("div", { cls: "sbl-welcome-steps" });379 const step1 = steps.createDiv({ cls: "sbl-welcome-step" });380 step1.createEl("strong", { text: "1. Open your vault from sublimated" });381 step1.createEl("p", {382 text: "In Finder, open your sublimated folder (under CloudStorage) as an Obsidian vault. The plugin detects the .sbl/ folder automatically."383 });384 const step2 = steps.createDiv({ cls: "sbl-welcome-step" });385 step2.createEl("strong", { text: "2. Status bar shows sync info" });386 step2.createEl("p", {387 text: "The bottom bar shows version number, sync status, and author for the current file \u2014 all read from local file metadata, no account needed."388 });389 const step3 = steps.createDiv({ cls: "sbl-welcome-step" });390 step3.createEl("strong", { text: "3. Add an API token for version history" });391 step3.createEl("p", {392 text: "To browse past versions, add your API token in Settings \u2192 sublimated. Generate one at sublimated.com/settings/tokens."393 });394 const links = contentEl.createDiv({ cls: "sbl-welcome-links" });395 links.createEl("a", {396 text: "Learn more at sublimated.com",397 href: "https://sublimated.com"398 });399 links.createEl("br");400 links.createEl("a", {401 text: "Documentation",402 href: "https://sublimated.com/docs/obsidian"403 });404 const footer = contentEl.createDiv({ cls: "sbl-welcome-footer" });405 footer.createEl("button", {406 text: "Get started",407 cls: "mod-cta"408 }).addEventListener("click", () => this.close());409 }410 onClose() {411 this.contentEl.empty();412 }413};414
415// src/main.ts416var SublimatedPlugin = class extends import_obsidian6.Plugin {417 settings = DEFAULT_SETTINGS;418 lastMetadata = null;419 statusBarEl = null;420 isSublimatedVault = false;421 async onload() {422 await this.loadSettings();423 this.isSublimatedVault = await this.checkSublimatedVault();424 this.statusBarEl = this.addStatusBarItem();425 this.statusBarEl.addClass("sbl-status-bar");426 this.statusBarEl.addEventListener(427 "click",428 () => this.openCurrentFileOnWeb()429 );430 this.updateStatusBar(null);431 this.registerView(432 VERSION_VIEW_TYPE,433 (leaf) => new VersionView(leaf, this)434 );435 this.addCommand({436 id: "open-on-web",437 name: "Open on sublimated.com",438 callback: () => this.openCurrentFileOnWeb()439 });440 this.addCommand({441 id: "show-versions",442 name: "Show versions",443 callback: () => this.activateVersionView()444 });445 this.addCommand({446 id: "copy-web-link",447 name: "Copy web link",448 callback: () => this.copyWebLink()449 });450 this.addRibbonIcon("history", "sublimated versions", () => {451 this.activateVersionView();452 });453 this.registerObsidianProtocolHandler(454 "sublimated",455 (params) => this.handleUri(params)456 );457 this.addSettingTab(new SublimatedSettingTab(this.app, this));458 this.registerEvent(459 this.app.workspace.on("active-leaf-change", () => {460 this.refreshMetadata();461 })462 );463 this.refreshMetadata();464 if (!this.settings.hasSeenWelcome) {465 new WelcomeModal(this.app).open();466 this.settings.hasSeenWelcome = true;467 await this.saveSettings();468 }469 }470 onunload() {471 this.app.workspace.detachLeavesOfType(VERSION_VIEW_TYPE);472 }473 async loadSettings() {474 this.settings = Object.assign(475 {},476 DEFAULT_SETTINGS,477 await this.loadData()478 );479 }480 async saveSettings() {481 await this.saveData(this.settings);482 }483 // check for .sbl/ folder in vault root484 async checkSublimatedVault() {485 const adapter = this.app.vault.adapter;486 return adapter.exists((0, import_obsidian6.normalizePath)(".sbl"));487 }488 detectWorkspace() {489 if (!this.isSublimatedVault) return null;490 return {491 space: this.lastMetadata?.space ?? null492 };493 }494 async refreshMetadata() {495 const file = this.app.workspace.getActiveFile();496 if (!file) {497 this.lastMetadata = null;498 this.updateStatusBar(null);499 return;500 }501 if (!isMacOS()) {502 this.lastMetadata = null;503 this.updateStatusBar(null);504 return;505 }506 const adapter = this.app.vault.adapter;507 const basePath = adapter.getBasePath?.();508 if (!basePath) {509 this.lastMetadata = null;510 this.updateStatusBar(null);511 return;512 }513 const absPath = `${basePath}/${file.path}`;514 this.lastMetadata = await readFileMetadata(absPath);515 this.updateStatusBar(this.lastMetadata);516 }517 updateStatusBar(meta) {518 if (!this.statusBarEl) return;519 if (!this.isSublimatedVault) {520 this.statusBarEl.setText("");521 return;522 }523 if (!meta) {524 this.statusBarEl.setText("sublimated");525 return;526 }527 const parts = [];528 if (meta.version) parts.push(`v${meta.version}`);529 if (meta.syncStatus) parts.push(meta.syncStatus);530 if (meta.modifiedBy) parts.push(`by ${meta.modifiedBy}`);531 if (parts.length > 0) {532 this.statusBarEl.setText(`sublimated: ${parts.join(" \xB7 ")}`);533 } else {534 this.statusBarEl.setText("sublimated");535 }536 if (meta.url) {537 this.statusBarEl.addClass("sbl-clickable");538 } else {539 this.statusBarEl.removeClass("sbl-clickable");540 }541 }542 openCurrentFileOnWeb() {543 const url = this.lastMetadata?.url;544 if (!url) {545 new import_obsidian6.Notice("No sublimated URL for this file");546 return;547 }548 window.open(`https://${url}`);549 }550 async copyWebLink() {551 const url = this.lastMetadata?.url;552 if (!url) {553 new import_obsidian6.Notice("No sublimated URL for this file");554 return;555 }556 await navigator.clipboard.writeText(`https://${url}`);557 new import_obsidian6.Notice("Link copied to clipboard");558 }559 async activateVersionView() {560 const existing = this.app.workspace.getLeavesOfType(VERSION_VIEW_TYPE);561 if (existing.length > 0) {562 this.app.workspace.revealLeaf(existing[0]);563 return;564 }565 const leaf = this.app.workspace.getRightLeaf(false);566 if (!leaf) return;567 await leaf.setViewState({568 type: VERSION_VIEW_TYPE,569 active: true570 });571 this.app.workspace.revealLeaf(leaf);572 }573 handleUri(params) {574 const { action, file } = params;575 if (action === "open" && file) {576 const normalized = (0, import_obsidian6.normalizePath)(file);577 const existing = this.app.vault.getAbstractFileByPath(normalized);578 if (existing) {579 this.app.workspace.openLinkText(normalized, "", false);580 } else {581 new import_obsidian6.Notice(`File not found: ${file}`);582 }583 return;584 }585 if (action === "auth" && params.token) {586 this.settings.apiToken = params.token;587 this.saveSettings();588 new import_obsidian6.Notice("sublimated: signed in successfully");589 return;590 }591 new import_obsidian6.Notice(`Unknown sublimated action: ${action}`);592 }593};594//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL21haW4udHMiLCAic3JjL3NldHRpbmdzLnRzIiwgInNyYy9hcGkudHMiLCAic3JjL3hhdHRyLnRzIiwgInNyYy92ZXJzaW9uLXZpZXcudHMiLCAic3JjL3dlbGNvbWUtbW9kYWwudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImltcG9ydCB7XG5cdE5vdGljZSxcblx0UGx1Z2luLFxuXHRXb3Jrc3BhY2VMZWFmLFxuXHRub3JtYWxpemVQYXRoLFxufSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB7XG5cdHR5cGUgU3VibGltYXRlZFNldHRpbmdzLFxuXHRERUZBVUxUX1NFVFRJTkdTLFxuXHRTdWJsaW1hdGVkU2V0dGluZ1RhYixcbn0gZnJvbSBcIi4vc2V0dGluZ3NcIjtcbmltcG9ydCB7IHR5cGUgRmlsZU1ldGFkYXRhLCByZWFkRmlsZU1ldGFkYXRhLCBpc01hY09TIH0gZnJvbSBcIi4veGF0dHJcIjtcbmltcG9ydCB7IFZlcnNpb25WaWV3LCBWRVJTSU9OX1ZJRVdfVFlQRSB9IGZyb20gXCIuL3ZlcnNpb24tdmlld1wiO1xuaW1wb3J0IHsgV2VsY29tZU1vZGFsIH0gZnJvbSBcIi4vd2VsY29tZS1tb2RhbFwiO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTdWJsaW1hdGVkUGx1Z2luIGV4dGVuZHMgUGx1Z2luIHtcblx0c2V0dGluZ3M6IFN1YmxpbWF0ZWRTZXR0aW5ncyA9IERFRkFVTFRfU0VUVElOR1M7XG5cdGxhc3RNZXRhZGF0YTogRmlsZU1ldGFkYXRhIHwgbnVsbCA9IG51bGw7XG5cblx0cHJpdmF0ZSBzdGF0dXNCYXJFbDogSFRNTEVsZW1lbnQgfCBudWxsID0gbnVsbDtcblx0cHJpdmF0ZSBpc1N1YmxpbWF0ZWRWYXVsdCA9IGZhbHNlO1xuXG5cdGFzeW5jIG9ubG9hZCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRhd2FpdCB0aGlzLmxvYWRTZXR0aW5ncygpO1xuXG5cdFx0Ly8gZGV0ZWN0IGlmIHRoaXMgdmF1bHQgaXMgYSBzdWJsaW1hdGVkIHdvcmtzcGFjZVxuXHRcdHRoaXMuaXNTdWJsaW1hdGVkVmF1bHQgPSBhd2FpdCB0aGlzLmNoZWNrU3VibGltYXRlZFZhdWx0KCk7XG5cblx0XHQvLyBzdGF0dXMgYmFyXG5cdFx0dGhpcy5zdGF0dXNCYXJFbCA9IHRoaXMuYWRkU3RhdHVzQmFySXRlbSgpO1xuXHRcdHRoaXMuc3RhdHVzQmFyRWwuYWRkQ2xhc3MoXCJzYmwtc3RhdHVzLWJhclwiKTtcblx0XHR0aGlzLnN0YXR1c0JhckVsLmFkZEV2ZW50TGlzdGVuZXIoXCJjbGlja1wiLCAoKSA9PlxuXHRcdFx0dGhpcy5vcGVuQ3VycmVudEZpbGVPbldlYigpXG5cdFx0KTtcblx0XHR0aGlzLnVwZGF0ZVN0YXR1c0JhcihudWxsKTtcblxuXHRcdC8vIHJlZ2lzdGVyIHZlcnNpb24gaGlzdG9yeSB2aWV3XG5cdFx0dGhpcy5yZWdpc3RlclZpZXcoXG5cdFx0XHRWRVJTSU9OX1ZJRVdfVFlQRSxcblx0XHRcdChsZWFmKSA9PiBuZXcgVmVyc2lvblZpZXcobGVhZiwgdGhpcylcblx0XHQpO1xuXG5cdFx0Ly8gY29tbWFuZHNcblx0XHR0aGlzLmFkZENvbW1hbmQoe1xuXHRcdFx0aWQ6IFwib3Blbi1vbi13ZWJcIixcblx0XHRcdG5hbWU6IFwiT3BlbiBvbiBzdWJsaW1hdGVkLmNvbVwiLFxuXHRcdFx0Y2FsbGJhY2s6ICgpID0+IHRoaXMub3BlbkN1cnJlbnRGaWxlT25XZWIoKSxcblx0XHR9KTtcblxuXHRcdHRoaXMuYWRkQ29tbWFuZCh7XG5cdFx0XHRpZDogXCJzaG93LXZlcnNpb25zXCIsXG5cdFx0XHRuYW1lOiBcIlNob3cgdmVyc2lvbnNcIixcblx0XHRcdGNhbGxiYWNrOiAoKSA9PiB0aGlzLmFjdGl2YXRlVmVyc2lvblZpZXcoKSxcblx0XHR9KTtcblxuXHRcdHRoaXMuYWRkQ29tbWFuZCh7XG5cdFx0XHRpZDogXCJjb3B5LXdlYi1saW5rXCIsXG5cdFx0XHRuYW1lOiBcIkNvcHkgd2ViIGxpbmtcIixcblx0XHRcdGNhbGxiYWNrOiAoKSA9PiB0aGlzLmNvcHlXZWJMaW5rKCksXG5cdFx0fSk7XG5cblx0XHQvLyByaWJib24gaWNvbiBmb3IgdmVyc2lvbiBzaWRlYmFyXG5cdFx0dGhpcy5hZGRSaWJib25JY29uKFwiaGlzdG9yeVwiLCBcInN1YmxpbWF0ZWQgdmVyc2lvbnNcIiwgKCkgPT4ge1xuXHRcdFx0dGhpcy5hY3RpdmF0ZVZlcnNpb25WaWV3KCk7XG5cdFx0fSk7XG5cblx0XHQvLyBVUkkgaGFuZGxlcjogb2JzaWRpYW46Ly9zdWJsaW1hdGVkP2FjdGlvbj1vcGVuJmZpbGU9cGF0aC90by9ub3RlLm1kXG5cdFx0dGhpcy5yZWdpc3Rlck9ic2lkaWFuUHJvdG9jb2xIYW5kbGVyKFxuXHRcdFx0XCJzdWJsaW1hdGVkXCIsXG5cdFx0XHQocGFyYW1zKSA9PiB0aGlzLmhhbmRsZVVyaShwYXJhbXMpXG5cdFx0KTtcblxuXHRcdC8vIHNldHRpbmdzIHRhYlxuXHRcdHRoaXMuYWRkU2V0dGluZ1RhYihuZXcgU3VibGltYXRlZFNldHRpbmdUYWIodGhpcy5hcHAsIHRoaXMpKTtcblxuXHRcdC8vIHVwZGF0ZSBzdGF0dXMgYmFyIHdoZW4gYWN0aXZlIGZpbGUgY2hhbmdlc1xuXHRcdHRoaXMucmVnaXN0ZXJFdmVudChcblx0XHRcdHRoaXMuYXBwLndvcmtzcGFjZS5vbihcImFjdGl2ZS1sZWFmLWNoYW5nZVwiLCAoKSA9PiB7XG5cdFx0XHRcdHRoaXMucmVmcmVzaE1ldGFkYXRhKCk7XG5cdFx0XHR9KVxuXHRcdCk7XG5cblx0XHQvLyBpbml0aWFsIG1ldGFkYXRhIHJlYWRcblx0XHR0aGlzLnJlZnJlc2hNZXRhZGF0YSgpO1xuXG5cdFx0Ly8gd2VsY29tZSBtb2RhbCBvbiBmaXJzdCBpbnN0YWxsXG5cdFx0aWYgKCF0aGlzLnNldHRpbmdzLmhhc1NlZW5XZWxjb21lKSB7XG5cdFx0XHRuZXcgV2VsY29tZU1vZGFsKHRoaXMuYXBwKS5vcGVuKCk7XG5cdFx0XHR0aGlzLnNldHRpbmdzLmhhc1NlZW5XZWxjb21lID0gdHJ1ZTtcblx0XHRcdGF3YWl0IHRoaXMuc2F2ZVNldHRpbmdzKCk7XG5cdFx0fVxuXHR9XG5cblx0b251bmxvYWQoKTogdm9pZCB7XG5cdFx0dGhpcy5hcHAud29ya3NwYWNlLmRldGFjaExlYXZlc09mVHlwZShWRVJTSU9OX1ZJRVdfVFlQRSk7XG5cdH1cblxuXHRhc3luYyBsb2FkU2V0dGluZ3MoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0dGhpcy5zZXR0aW5ncyA9IE9iamVjdC5hc3NpZ24oXG5cdFx0XHR7fSxcblx0XHRcdERFRkFVTFRfU0VUVElOR1MsXG5cdFx0XHRhd2FpdCB0aGlzLmxvYWREYXRhKClcblx0XHQpO1xuXHR9XG5cblx0YXN5bmMgc2F2ZVNldHRpbmdzKCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGF3YWl0IHRoaXMuc2F2ZURhdGEodGhpcy5zZXR0aW5ncyk7XG5cdH1cblxuXHQvLyBjaGVjayBmb3IgLnNibC8gZm9sZGVyIGluIHZhdWx0IHJvb3Rcblx0cHJpdmF0ZSBhc3luYyBjaGVja1N1YmxpbWF0ZWRWYXVsdCgpOiBQcm9taXNlPGJvb2xlYW4+IHtcblx0XHRjb25zdCBhZGFwdGVyID0gdGhpcy5hcHAudmF1bHQuYWRhcHRlcjtcblx0XHRyZXR1cm4gYWRhcHRlci5leGlzdHMobm9ybWFsaXplUGF0aChcIi5zYmxcIikpO1xuXHR9XG5cblx0ZGV0ZWN0V29ya3NwYWNlKCk6IHsgc3BhY2U6IHN0cmluZyB8IG51bGwgfSB8IG51bGwge1xuXHRcdGlmICghdGhpcy5pc1N1YmxpbWF0ZWRWYXVsdCkgcmV0dXJuIG51bGw7XG5cdFx0cmV0dXJuIHtcblx0XHRcdHNwYWNlOiB0aGlzLmxhc3RNZXRhZGF0YT8uc3BhY2UgPz8gbnVsbCxcblx0XHR9O1xuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyByZWZyZXNoTWV0YWRhdGEoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0Y29uc3QgZmlsZSA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRBY3RpdmVGaWxlKCk7XG5cdFx0aWYgKCFmaWxlKSB7XG5cdFx0XHR0aGlzLmxhc3RNZXRhZGF0YSA9IG51bGw7XG5cdFx0XHR0aGlzLnVwZGF0ZVN0YXR1c0JhcihudWxsKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIWlzTWFjT1MoKSkge1xuXHRcdFx0dGhpcy5sYXN0TWV0YWRhdGEgPSBudWxsO1xuXHRcdFx0dGhpcy51cGRhdGVTdGF0dXNCYXIobnVsbCk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gZ2V0IGFic29sdXRlIHBhdGggdmlhIHZhdWx0IGFkYXB0ZXJcblx0XHRjb25zdCBhZGFwdGVyID0gdGhpcy5hcHAudmF1bHQuYWRhcHRlcjtcblx0XHRjb25zdCBiYXNlUGF0aCA9IChhZGFwdGVyIGFzIHsgZ2V0QmFzZVBhdGg/OiAoKSA9PiBzdHJpbmcgfSlcblx0XHRcdC5nZXRCYXNlUGF0aD8uKCk7XG5cdFx0aWYgKCFiYXNlUGF0aCkge1xuXHRcdFx0dGhpcy5sYXN0TWV0YWRhdGEgPSBudWxsO1xuXHRcdFx0dGhpcy51cGRhdGVTdGF0dXNCYXIobnVsbCk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Y29uc3QgYWJzUGF0aCA9IGAke2Jhc2VQYXRofS8ke2ZpbGUucGF0aH1gO1xuXHRcdHRoaXMubGFzdE1ldGFkYXRhID0gYXdhaXQgcmVhZEZpbGVNZXRhZGF0YShhYnNQYXRoKTtcblx0XHR0aGlzLnVwZGF0ZVN0YXR1c0Jhcih0aGlzLmxhc3RNZXRhZGF0YSk7XG5cdH1cblxuXHRwcml2YXRlIHVwZGF0ZVN0YXR1c0JhcihtZXRhOiBGaWxlTWV0YWRhdGEgfCBudWxsKTogdm9pZCB7XG5cdFx0aWYgKCF0aGlzLnN0YXR1c0JhckVsKSByZXR1cm47XG5cblx0XHRpZiAoIXRoaXMuaXNTdWJsaW1hdGVkVmF1bHQpIHtcblx0XHRcdHRoaXMuc3RhdHVzQmFyRWwuc2V0VGV4dChcIlwiKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIW1ldGEpIHtcblx0XHRcdHRoaXMuc3RhdHVzQmFyRWwuc2V0VGV4dChcInN1YmxpbWF0ZWRcIik7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Y29uc3QgcGFydHM6IHN0cmluZ1tdID0gW107XG5cdFx0aWYgKG1ldGEudmVyc2lvbikgcGFydHMucHVzaChgdiR7bWV0YS52ZXJzaW9ufWApO1xuXHRcdGlmIChtZXRhLnN5bmNTdGF0dXMpIHBhcnRzLnB1c2gobWV0YS5zeW5jU3RhdHVzKTtcblx0XHRpZiAobWV0YS5tb2RpZmllZEJ5KSBwYXJ0cy5wdXNoKGBieSAke21ldGEubW9kaWZpZWRCeX1gKTtcblxuXHRcdGlmIChwYXJ0cy5sZW5ndGggPiAwKSB7XG5cdFx0XHR0aGlzLnN0YXR1c0JhckVsLnNldFRleHQoYHN1YmxpbWF0ZWQ6ICR7cGFydHMuam9pbihcIiBcdTAwQjcgXCIpfWApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLnN0YXR1c0JhckVsLnNldFRleHQoXCJzdWJsaW1hdGVkXCIpO1xuXHRcdH1cblxuXHRcdC8vIHNob3cgcG9pbnRlciBjdXJzb3Igd2hlbiBVUkwgYXZhaWxhYmxlXG5cdFx0aWYgKG1ldGEudXJsKSB7XG5cdFx0XHR0aGlzLnN0YXR1c0JhckVsLmFkZENsYXNzKFwic2JsLWNsaWNrYWJsZVwiKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5zdGF0dXNCYXJFbC5yZW1vdmVDbGFzcyhcInNibC1jbGlja2FibGVcIik7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBvcGVuQ3VycmVudEZpbGVPbldlYigpOiB2b2lkIHtcblx0XHRjb25zdCB1cmwgPSB0aGlzLmxhc3RNZXRhZGF0YT8udXJsO1xuXHRcdGlmICghdXJsKSB7XG5cdFx0XHRuZXcgTm90aWNlKFwiTm8gc3VibGltYXRlZCBVUkwgZm9yIHRoaXMgZmlsZVwiKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0d2luZG93Lm9wZW4oYGh0dHBzOi8vJHt1cmx9YCk7XG5cdH1cblxuXHRwcml2YXRlIGFzeW5jIGNvcHlXZWJMaW5rKCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGNvbnN0IHVybCA9IHRoaXMubGFzdE1ldGFkYXRhPy51cmw7XG5cdFx0aWYgKCF1cmwpIHtcblx0XHRcdG5ldyBOb3RpY2UoXCJObyBzdWJsaW1hdGVkIFVSTCBmb3IgdGhpcyBmaWxlXCIpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRhd2FpdCBuYXZpZ2F0b3IuY2xpcGJvYXJkLndyaXRlVGV4dChgaHR0cHM6Ly8ke3VybH1gKTtcblx0XHRuZXcgTm90aWNlKFwiTGluayBjb3BpZWQgdG8gY2xpcGJvYXJkXCIpO1xuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyBhY3RpdmF0ZVZlcnNpb25WaWV3KCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGNvbnN0IGV4aXN0aW5nID1cblx0XHRcdHRoaXMuYXBwLndvcmtzcGFjZS5nZXRMZWF2ZXNPZlR5cGUoVkVSU0lPTl9WSUVXX1RZUEUpO1xuXHRcdGlmIChleGlzdGluZy5sZW5ndGggPiAwKSB7XG5cdFx0XHR0aGlzLmFwcC53b3Jrc3BhY2UucmV2ZWFsTGVhZihleGlzdGluZ1swXSk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Y29uc3QgbGVhZiA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRSaWdodExlYWYoZmFsc2UpO1xuXHRcdGlmICghbGVhZikgcmV0dXJuO1xuXHRcdGF3YWl0IGxlYWYuc2V0Vmlld1N0YXRlKHtcblx0XHRcdHR5cGU6IFZFUlNJT05fVklFV19UWVBFLFxuXHRcdFx0YWN0aXZlOiB0cnVlLFxuXHRcdH0pO1xuXHRcdHRoaXMuYXBwLndvcmtzcGFjZS5yZXZlYWxMZWFmKGxlYWYpO1xuXHR9XG5cblx0cHJpdmF0ZSBoYW5kbGVVcmkocGFyYW1zOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+KTogdm9pZCB7XG5cdFx0Y29uc3QgeyBhY3Rpb24sIGZpbGUgfSA9IHBhcmFtcztcblxuXHRcdGlmIChhY3Rpb24gPT09IFwib3BlblwiICYmIGZpbGUpIHtcblx0XHRcdGNvbnN0IG5vcm1hbGl6ZWQgPSBub3JtYWxpemVQYXRoKGZpbGUpO1xuXHRcdFx0Y29uc3QgZXhpc3RpbmcgPSB0aGlzLmFwcC52YXVsdC5nZXRBYnN0cmFjdEZpbGVCeVBhdGgobm9ybWFsaXplZCk7XG5cdFx0XHRpZiAoZXhpc3RpbmcpIHtcblx0XHRcdFx0dGhpcy5hcHAud29ya3NwYWNlLm9wZW5MaW5rVGV4dChub3JtYWxpemVkLCBcIlwiLCBmYWxzZSk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRuZXcgTm90aWNlKGBGaWxlIG5vdCBmb3VuZDogJHtmaWxlfWApO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlmIChhY3Rpb24gPT09IFwiYXV0aFwiICYmIHBhcmFtcy50b2tlbikge1xuXHRcdFx0Ly8gZnV0dXJlOiBPQXV0aCBmbG93IHN0b3JlcyB0b2tlbiBmcm9tIHJlZGlyZWN0XG5cdFx0XHR0aGlzLnNldHRpbmdzLmFwaVRva2VuID0gcGFyYW1zLnRva2VuO1xuXHRcdFx0dGhpcy5zYXZlU2V0dGluZ3MoKTtcblx0XHRcdG5ldyBOb3RpY2UoXCJzdWJsaW1hdGVkOiBzaWduZWQgaW4gc3VjY2Vzc2Z1bGx5XCIpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdG5ldyBOb3RpY2UoYFVua25vd24gc3VibGltYXRlZCBhY3Rpb246ICR7YWN0aW9ufWApO1xuXHR9XG59XG4iLCAiaW1wb3J0IHsgQXBwLCBOb3RpY2UsIFBsdWdpblNldHRpbmdUYWIsIFNldHRpbmcgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCB0eXBlIFN1YmxpbWF0ZWRQbHVnaW4gZnJvbSBcIi4vbWFpblwiO1xuaW1wb3J0IHsgU3VibGltYXRlZEFwaSB9IGZyb20gXCIuL2FwaVwiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFN1YmxpbWF0ZWRTZXR0aW5ncyB7XG5cdGFwaVRva2VuOiBzdHJpbmc7XG5cdGhhc1NlZW5XZWxjb21lOiBib29sZWFuO1xufVxuXG5leHBvcnQgY29uc3QgREVGQVVMVF9TRVRUSU5HUzogU3VibGltYXRlZFNldHRpbmdzID0ge1xuXHRhcGlUb2tlbjogXCJcIixcblx0aGFzU2VlbldlbGNvbWU6IGZhbHNlLFxufTtcblxuZXhwb3J0IGNsYXNzIFN1YmxpbWF0ZWRTZXR0aW5nVGFiIGV4dGVuZHMgUGx1Z2luU2V0dGluZ1RhYiB7XG5cdHBsdWdpbjogU3VibGltYXRlZFBsdWdpbjtcblxuXHRjb25zdHJ1Y3RvcihhcHA6IEFwcCwgcGx1Z2luOiBTdWJsaW1hdGVkUGx1Z2luKSB7XG5cdFx0c3VwZXIoYXBwLCBwbHVnaW4pO1xuXHRcdHRoaXMucGx1Z2luID0gcGx1Z2luO1xuXHR9XG5cblx0ZGlzcGxheSgpOiB2b2lkIHtcblx0XHRjb25zdCB7IGNvbnRhaW5lckVsIH0gPSB0aGlzO1xuXHRcdGNvbnRhaW5lckVsLmVtcHR5KCk7XG5cblx0XHQvLyBicmFuZGluZ1xuXHRcdGNvbnN0IGhlYWRlciA9IGNvbnRhaW5lckVsLmNyZWF0ZURpdih7IGNsczogXCJzYmwtc2V0dGluZ3MtaGVhZGVyXCIgfSk7XG5cdFx0aGVhZGVyLmNyZWF0ZUVsKFwiaDJcIiwgeyB0ZXh0OiBcInN1YmxpbWF0ZWRcIiB9KTtcblx0XHRoZWFkZXIuY3JlYXRlRWwoXCJwXCIsIHtcblx0XHRcdHRleHQ6IFwiU3luYyBzdGF0dXMsIHZlcnNpb24gaGlzdG9yeSwgYW5kIGRlZXAgbGlua3MuXCIsXG5cdFx0XHRjbHM6IFwic2V0dGluZy1pdGVtLWRlc2NyaXB0aW9uXCIsXG5cdFx0fSk7XG5cblx0XHQvLyB3b3Jrc3BhY2UgZGV0ZWN0aW9uXG5cdFx0Y29uc3Qgd29ya3NwYWNlSW5mbyA9IHRoaXMucGx1Z2luLmRldGVjdFdvcmtzcGFjZSgpO1xuXHRcdGlmICh3b3Jrc3BhY2VJbmZvKSB7XG5cdFx0XHRuZXcgU2V0dGluZyhjb250YWluZXJFbClcblx0XHRcdFx0LnNldE5hbWUoXCJEZXRlY3RlZCB3b3Jrc3BhY2VcIilcblx0XHRcdFx0LnNldERlc2MoXG5cdFx0XHRcdFx0YFNwYWNlOiAke3dvcmtzcGFjZUluZm8uc3BhY2UgfHwgXCJ1bmtub3duXCJ9IFx1MjAxNCAuc2JsLyBmb2xkZXIgZm91bmQgaW4gdmF1bHQgcm9vdGBcblx0XHRcdFx0KTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0bmV3IFNldHRpbmcoY29udGFpbmVyRWwpXG5cdFx0XHRcdC5zZXROYW1lKFwiTm8gd29ya3NwYWNlIGRldGVjdGVkXCIpXG5cdFx0XHRcdC5zZXREZXNjKFxuXHRcdFx0XHRcdFwiTm8gLnNibC8gZm9sZGVyIGZvdW5kLiBPcGVuIGEgdmF1bHQgZnJvbSB5b3VyIHN1YmxpbWF0ZWQgZm9sZGVyIGluIEZpbmRlci5cIlxuXHRcdFx0XHQpO1xuXHRcdH1cblxuXHRcdC8vIEFQSSB0b2tlblxuXHRcdG5ldyBTZXR0aW5nKGNvbnRhaW5lckVsKVxuXHRcdFx0LnNldE5hbWUoXCJBUEkgdG9rZW5cIilcblx0XHRcdC5zZXREZXNjKFxuXHRcdFx0XHRcIlJlcXVpcmVkIGZvciB2ZXJzaW9uIGhpc3RvcnkuIEdlbmVyYXRlIGF0IHN1YmxpbWF0ZWQuY29tL3NldHRpbmdzL3Rva2Vuc1wiXG5cdFx0XHQpXG5cdFx0XHQuYWRkVGV4dCgodGV4dCkgPT5cblx0XHRcdFx0dGV4dFxuXHRcdFx0XHRcdC5zZXRQbGFjZWhvbGRlcihcInNrXy4uLlwiKVxuXHRcdFx0XHRcdC5zZXRWYWx1ZSh0aGlzLnBsdWdpbi5zZXR0aW5ncy5hcGlUb2tlbilcblx0XHRcdFx0XHQub25DaGFuZ2UoYXN5bmMgKHZhbHVlKSA9PiB7XG5cdFx0XHRcdFx0XHR0aGlzLnBsdWdpbi5zZXR0aW5ncy5hcGlUb2tlbiA9IHZhbHVlO1xuXHRcdFx0XHRcdFx0YXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKCk7XG5cdFx0XHRcdFx0fSlcblx0XHRcdCk7XG5cblx0XHQvLyB0ZXN0IGNvbm5lY3Rpb25cblx0XHRuZXcgU2V0dGluZyhjb250YWluZXJFbClcblx0XHRcdC5zZXROYW1lKFwiVGVzdCBjb25uZWN0aW9uXCIpXG5cdFx0XHQuc2V0RGVzYyhcIlZlcmlmeSB5b3VyIEFQSSB0b2tlbiB3b3Jrc1wiKVxuXHRcdFx0LmFkZEJ1dHRvbigoYnRuKSA9PlxuXHRcdFx0XHRidG4uc2V0QnV0dG9uVGV4dChcIlRlc3RcIikub25DbGljayhhc3luYyAoKSA9PiB7XG5cdFx0XHRcdFx0aWYgKCF0aGlzLnBsdWdpbi5zZXR0aW5ncy5hcGlUb2tlbikge1xuXHRcdFx0XHRcdFx0bmV3IE5vdGljZShcIk5vIEFQSSB0b2tlbiBjb25maWd1cmVkXCIpO1xuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRidG4uc2V0QnV0dG9uVGV4dChcIlRlc3RpbmcuLi5cIik7XG5cdFx0XHRcdFx0YnRuLnNldERpc2FibGVkKHRydWUpO1xuXHRcdFx0XHRcdGNvbnN0IGFwaSA9IG5ldyBTdWJsaW1hdGVkQXBpKFxuXHRcdFx0XHRcdFx0dGhpcy5wbHVnaW4uc2V0dGluZ3MuYXBpVG9rZW5cblx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdGNvbnN0IHJlc3VsdCA9IGF3YWl0IGFwaS50ZXN0Q29ubmVjdGlvbigpO1xuXHRcdFx0XHRcdGlmIChyZXN1bHQub2spIHtcblx0XHRcdFx0XHRcdG5ldyBOb3RpY2UoXG5cdFx0XHRcdFx0XHRcdGBDb25uZWN0ZWQgYXMgJHtyZXN1bHQudXNlcm5hbWV9YFxuXHRcdFx0XHRcdFx0KTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0bmV3IE5vdGljZShcblx0XHRcdFx0XHRcdFx0YENvbm5lY3Rpb24gZmFpbGVkOiAke3Jlc3VsdC5lcnJvcn1gXG5cdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRidG4uc2V0QnV0dG9uVGV4dChcIlRlc3RcIik7XG5cdFx0XHRcdFx0YnRuLnNldERpc2FibGVkKGZhbHNlKTtcblx0XHRcdFx0fSlcblx0XHRcdCk7XG5cblx0XHQvLyBsaW5rc1xuXHRcdGNvbnRhaW5lckVsLmNyZWF0ZUVsKFwiZGl2XCIsIHsgY2xzOiBcInNibC1zZXR0aW5ncy1saW5rc1wiIH0sIChlbCkgPT4ge1xuXHRcdFx0ZWwuY3JlYXRlRWwoXCJhXCIsIHtcblx0XHRcdFx0dGV4dDogXCJzdWJsaW1hdGVkLmNvbVwiLFxuXHRcdFx0XHRocmVmOiBcImh0dHBzOi8vc3VibGltYXRlZC5jb21cIixcblx0XHRcdH0pO1xuXHRcdFx0ZWwuY3JlYXRlRWwoXCJzcGFuXCIsIHsgdGV4dDogXCIgXHUwMEI3IFwiIH0pO1xuXHRcdFx0ZWwuY3JlYXRlRWwoXCJhXCIsIHtcblx0XHRcdFx0dGV4dDogXCJEb2N1bWVudGF0aW9uXCIsXG5cdFx0XHRcdGhyZWY6IFwiaHR0cHM6Ly9zdWJsaW1hdGVkLmNvbS9kb2NzL29ic2lkaWFuXCIsXG5cdFx0XHR9KTtcblx0XHRcdGVsLmNyZWF0ZUVsKFwic3BhblwiLCB7IHRleHQ6IFwiIFx1MDBCNyBcIiB9KTtcblx0XHRcdGVsLmNyZWF0ZUVsKFwiYVwiLCB7XG5cdFx0XHRcdHRleHQ6IFwiUmVwb3J0IGFuIGlzc3VlXCIsXG5cdFx0XHRcdGhyZWY6IFwiaHR0cHM6Ly9naXRodWIuY29tL25pY2hvY2hhci9vYnNpZGlhbi1zdWJsaW1hdGVkL2lzc3Vlc1wiLFxuXHRcdFx0fSk7XG5cdFx0fSk7XG5cdH1cbn1cbiIsICJpbXBvcnQgeyByZXF1ZXN0VXJsIH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5cbmNvbnN0IEFQSV9CQVNFID0gXCJodHRwczovL2FwaS5zdWJsaW1hdGVkLmNvbVwiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFZlcnNpb24ge1xuXHRpZDogc3RyaW5nO1xuXHRudW1iZXI6IG51bWJlcjtcblx0YXV0aG9yTmFtZTogc3RyaW5nO1xuXHRhdXRob3JUeXBlOiBzdHJpbmc7XG5cdG1lc3NhZ2U6IHN0cmluZyB8IG51bGw7XG5cdGNyZWF0ZWRBdDogc3RyaW5nO1xuXHRjb21taXRIYXNoOiBzdHJpbmcgfCBudWxsO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFZlcnNpb25zUmVzcG9uc2Uge1xuXHR2ZXJzaW9uczogVmVyc2lvbltdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRlc3RDb25uZWN0aW9uUmVzdWx0IHtcblx0b2s6IGJvb2xlYW47XG5cdHVzZXJuYW1lPzogc3RyaW5nO1xuXHRlcnJvcj86IHN0cmluZztcbn1cblxuZXhwb3J0IGNsYXNzIFN1YmxpbWF0ZWRBcGkge1xuXHRjb25zdHJ1Y3Rvcihwcml2YXRlIHRva2VuOiBzdHJpbmcpIHt9XG5cblx0cHJpdmF0ZSBhc3luYyByZXF1ZXN0PFQ+KHBhdGg6IHN0cmluZyk6IFByb21pc2U8VD4ge1xuXHRcdGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgcmVxdWVzdFVybCh7XG5cdFx0XHR1cmw6IGAke0FQSV9CQVNFfSR7cGF0aH1gLFxuXHRcdFx0aGVhZGVyczoge1xuXHRcdFx0XHRBdXRob3JpemF0aW9uOiBgQmVhcmVyICR7dGhpcy50b2tlbn1gLFxuXHRcdFx0XHRBY2NlcHQ6IFwiYXBwbGljYXRpb24vanNvblwiLFxuXHRcdFx0fSxcblx0XHR9KTtcblxuXHRcdGlmIChyZXNwb25zZS5zdGF0dXMgPj0gNDAwKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoXG5cdFx0XHRcdGBBUEkgZXJyb3IgJHtyZXNwb25zZS5zdGF0dXN9OiAke3Jlc3BvbnNlLnRleHR9YFxuXHRcdFx0KTtcblx0XHR9XG5cblx0XHRyZXR1cm4gcmVzcG9uc2UuanNvbiBhcyBUO1xuXHR9XG5cblx0YXN5bmMgZ2V0VmVyc2lvbnMoXG5cdFx0c3BhY2VTbHVnOiBzdHJpbmcsXG5cdFx0aXRlbUlkOiBzdHJpbmdcblx0KTogUHJvbWlzZTxWZXJzaW9uW10+IHtcblx0XHRjb25zdCBkYXRhID0gYXdhaXQgdGhpcy5yZXF1ZXN0PFZlcnNpb25zUmVzcG9uc2U+KFxuXHRcdFx0YC92MS9zcGFjZXMvJHtlbmNvZGVVUklDb21wb25lbnQoc3BhY2VTbHVnKX0vaXRlbXMvJHtlbmNvZGVVUklDb21wb25lbnQoaXRlbUlkKX0vdmVyc2lvbnNgXG5cdFx0KTtcblx0XHRyZXR1cm4gZGF0YS52ZXJzaW9ucztcblx0fVxuXG5cdGFzeW5jIHRlc3RDb25uZWN0aW9uKCk6IFByb21pc2U8VGVzdENvbm5lY3Rpb25SZXN1bHQ+IHtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgZGF0YSA9IGF3YWl0IHRoaXMucmVxdWVzdDx7IHVzZXJuYW1lOiBzdHJpbmcgfT4oXG5cdFx0XHRcdFwiL3YxL21lXCJcblx0XHRcdCk7XG5cdFx0XHRyZXR1cm4geyBvazogdHJ1ZSwgdXNlcm5hbWU6IGRhdGEudXNlcm5hbWUgfTtcblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHRvazogZmFsc2UsXG5cdFx0XHRcdGVycm9yOiBlIGluc3RhbmNlb2YgRXJyb3IgPyBlLm1lc3NhZ2UgOiBcIlVua25vd24gZXJyb3JcIixcblx0XHRcdH07XG5cdFx0fVxuXHR9XG59XG4iLCAiaW1wb3J0IHsgZXhlYyB9IGZyb20gXCJjaGlsZF9wcm9jZXNzXCI7XG5pbXBvcnQgeyBQbGF0Zm9ybSB9IGZyb20gXCJvYnNpZGlhblwiO1xuXG5jb25zdCBYQVRUUl9QUkVGSVggPSBcImNvbS5zdWJsaW1hdGVkLlwiO1xuY29uc3QgWEFUVFJfU1VGRklYID0gXCIjUFNcIjtcblxuZXhwb3J0IGludGVyZmFjZSBGaWxlTWV0YWRhdGEge1xuXHR2ZXJzaW9uOiBzdHJpbmcgfCBudWxsO1xuXHRjb21taXQ6IHN0cmluZyB8IG51bGw7XG5cdHVybDogc3RyaW5nIHwgbnVsbDtcblx0Y29udGVudEhhc2g6IHN0cmluZyB8IG51bGw7XG5cdGlkOiBzdHJpbmcgfCBudWxsO1xuXHRjcmVhdGVkQnk6IHN0cmluZyB8IG51bGw7XG5cdG1vZGlmaWVkQnk6IHN0cmluZyB8IG51bGw7XG5cdG1vZGlmaWVkQnlUeXBlOiBzdHJpbmcgfCBudWxsO1xuXHRzeW5jU3RhdHVzOiBzdHJpbmcgfCBudWxsO1xuXHRzcGFjZTogc3RyaW5nIHwgbnVsbDtcblx0YnJhbmNoOiBzdHJpbmcgfCBudWxsO1xuXHRtb2RpZmllZEF0OiBzdHJpbmcgfCBudWxsO1xufVxuXG5jb25zdCBBVFRSUzogKGtleW9mIEZpbGVNZXRhZGF0YSlbXSA9IFtcblx0XCJ2ZXJzaW9uXCIsXG5cdFwiY29tbWl0XCIsXG5cdFwidXJsXCIsXG5cdFwiY29udGVudEhhc2hcIixcblx0XCJpZFwiLFxuXHRcImNyZWF0ZWRCeVwiLFxuXHRcIm1vZGlmaWVkQnlcIixcblx0XCJtb2RpZmllZEJ5VHlwZVwiLFxuXHRcInN5bmNTdGF0dXNcIixcblx0XCJzcGFjZVwiLFxuXHRcImJyYW5jaFwiLFxuXHRcIm1vZGlmaWVkQXRcIixcbl07XG5cbmZ1bmN0aW9uIGF0dHJUb1hhdHRyTmFtZShhdHRyOiBzdHJpbmcpOiBzdHJpbmcge1xuXHQvLyBjYW1lbENhc2UgXHUyMTkyIGtlYmFiLWNhc2U6IGNvbnRlbnRIYXNoIFx1MjE5MiBjb250ZW50LWhhc2hcblx0Y29uc3Qga2ViYWIgPSBhdHRyLnJlcGxhY2UoL1tBLVpdL2csIChjKSA9PiBcIi1cIiArIGMudG9Mb3dlckNhc2UoKSk7XG5cdHJldHVybiBgJHtYQVRUUl9QUkVGSVh9JHtrZWJhYn0ke1hBVFRSX1NVRkZJWH1gO1xufVxuXG5mdW5jdGlvbiByZWFkWGF0dHIocGF0aDogc3RyaW5nLCB4YXR0ck5hbWU6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuXHRyZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcblx0XHRleGVjKFxuXHRcdFx0YHhhdHRyIC1wICR7SlNPTi5zdHJpbmdpZnkoeGF0dHJOYW1lKX0gJHtKU09OLnN0cmluZ2lmeShwYXRoKX1gLFxuXHRcdFx0eyB0aW1lb3V0OiAyMDAwIH0sXG5cdFx0XHQoZXJyLCBzdGRvdXQpID0+IHtcblx0XHRcdFx0aWYgKGVycikge1xuXHRcdFx0XHRcdHJlc29sdmUobnVsbCk7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJlc29sdmUoc3Rkb3V0LnRyaW0oKSB8fCBudWxsKTtcblx0XHRcdH1cblx0XHQpO1xuXHR9KTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlYWRGaWxlTWV0YWRhdGEoXG5cdHBhdGg6IHN0cmluZ1xuKTogUHJvbWlzZTxGaWxlTWV0YWRhdGEgfCBudWxsPiB7XG5cdGlmICghUGxhdGZvcm0uaXNNYWNPUykgcmV0dXJuIG51bGw7XG5cblx0Y29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKFxuXHRcdEFUVFJTLm1hcCgoYXR0cikgPT4gcmVhZFhhdHRyKHBhdGgsIGF0dHJUb1hhdHRyTmFtZShhdHRyKSkpXG5cdCk7XG5cblx0Ly8gSWYgbm8geGF0dHJzIGZvdW5kIGF0IGFsbCwgdGhpcyBpc24ndCBhIHN1YmxpbWF0ZWQgZmlsZVxuXHRpZiAocmVzdWx0cy5ldmVyeSgocikgPT4gciA9PT0gbnVsbCkpIHJldHVybiBudWxsO1xuXG5cdGNvbnN0IG1ldGE6IEZpbGVNZXRhZGF0YSA9IHtcblx0XHR2ZXJzaW9uOiBudWxsLCBjb21taXQ6IG51bGwsIHVybDogbnVsbCwgY29udGVudEhhc2g6IG51bGwsXG5cdFx0aWQ6IG51bGwsIGNyZWF0ZWRCeTogbnVsbCwgbW9kaWZpZWRCeTogbnVsbCwgbW9kaWZpZWRCeVR5cGU6IG51bGwsXG5cdFx0c3luY1N0YXR1czogbnVsbCwgc3BhY2U6IG51bGwsIGJyYW5jaDogbnVsbCwgbW9kaWZpZWRBdDogbnVsbCxcblx0fTtcblx0QVRUUlMuZm9yRWFjaCgoYXR0ciwgaSkgPT4ge1xuXHRcdG1ldGFbYXR0cl0gPSByZXN1bHRzW2ldO1xuXHR9KTtcblx0cmV0dXJuIG1ldGE7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc01hY09TKCk6IGJvb2xlYW4ge1xuXHRyZXR1cm4gUGxhdGZvcm0uaXNNYWNPUztcbn1cbiIsICJpbXBvcnQgeyBJdGVtVmlldywgTm90aWNlLCBXb3Jrc3BhY2VMZWFmIH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5pbXBvcnQgdHlwZSBTdWJsaW1hdGVkUGx1Z2luIGZyb20gXCIuL21haW5cIjtcbmltcG9ydCB7IFN1YmxpbWF0ZWRBcGksIHR5cGUgVmVyc2lvbiB9IGZyb20gXCIuL2FwaVwiO1xuXG5leHBvcnQgY29uc3QgVkVSU0lPTl9WSUVXX1RZUEUgPSBcInN1YmxpbWF0ZWQtdmVyc2lvbnNcIjtcblxuZXhwb3J0IGNsYXNzIFZlcnNpb25WaWV3IGV4dGVuZHMgSXRlbVZpZXcge1xuXHRwbHVnaW46IFN1YmxpbWF0ZWRQbHVnaW47XG5cblx0Y29uc3RydWN0b3IobGVhZjogV29ya3NwYWNlTGVhZiwgcGx1Z2luOiBTdWJsaW1hdGVkUGx1Z2luKSB7XG5cdFx0c3VwZXIobGVhZik7XG5cdFx0dGhpcy5wbHVnaW4gPSBwbHVnaW47XG5cdH1cblxuXHRnZXRWaWV3VHlwZSgpOiBzdHJpbmcge1xuXHRcdHJldHVybiBWRVJTSU9OX1ZJRVdfVFlQRTtcblx0fVxuXG5cdGdldERpc3BsYXlUZXh0KCk6IHN0cmluZyB7XG5cdFx0cmV0dXJuIFwic3VibGltYXRlZCB2ZXJzaW9uc1wiO1xuXHR9XG5cblx0Z2V0SWNvbigpOiBzdHJpbmcge1xuXHRcdHJldHVybiBcImhpc3RvcnlcIjtcblx0fVxuXG5cdGFzeW5jIG9uT3BlbigpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRhd2FpdCB0aGlzLnJlZnJlc2goKTtcblx0XHQvLyByZWZyZXNoIHdoZW4gYWN0aXZlIGZpbGUgY2hhbmdlc1xuXHRcdHRoaXMucmVnaXN0ZXJFdmVudChcblx0XHRcdHRoaXMuYXBwLndvcmtzcGFjZS5vbihcImFjdGl2ZS1sZWFmLWNoYW5nZVwiLCAoKSA9PiB7XG5cdFx0XHRcdHRoaXMucmVmcmVzaCgpO1xuXHRcdFx0fSlcblx0XHQpO1xuXHR9XG5cblx0YXN5bmMgcmVmcmVzaCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRjb25zdCB7IGNvbnRlbnRFbCB9ID0gdGhpcztcblx0XHRjb250ZW50RWwuZW1wdHkoKTtcblx0XHRjb250ZW50RWwuYWRkQ2xhc3MoXCJzYmwtdmVyc2lvbi12aWV3XCIpO1xuXG5cdFx0Y29uc3QgYWN0aXZlRmlsZSA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRBY3RpdmVGaWxlKCk7XG5cdFx0aWYgKCFhY3RpdmVGaWxlKSB7XG5cdFx0XHRjb250ZW50RWwuY3JlYXRlRWwoXCJwXCIsIHtcblx0XHRcdFx0dGV4dDogXCJObyBmaWxlIG9wZW4uXCIsXG5cdFx0XHRcdGNsczogXCJzYmwtdmVyc2lvbi1lbXB0eVwiLFxuXHRcdFx0fSk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0aWYgKCF0aGlzLnBsdWdpbi5zZXR0aW5ncy5hcGlUb2tlbikge1xuXHRcdFx0Y29udGVudEVsLmNyZWF0ZUVsKFwicFwiLCB7XG5cdFx0XHRcdHRleHQ6IFwiQVBJIHRva2VuIHJlcXVpcmVkIGZvciB2ZXJzaW9uIGhpc3RvcnkuXCIsXG5cdFx0XHRcdGNsczogXCJzYmwtdmVyc2lvbi1lbXB0eVwiLFxuXHRcdFx0fSk7XG5cdFx0XHRjb250ZW50RWwuY3JlYXRlRWwoXCJwXCIsIHtcblx0XHRcdFx0dGV4dDogXCJDb25maWd1cmUgaW4gU2V0dGluZ3MgXHUyMTkyIHN1YmxpbWF0ZWRcIixcblx0XHRcdFx0Y2xzOiBcInNibC12ZXJzaW9uLWhpbnRcIixcblx0XHRcdH0pO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIHJlYWQgeGF0dHJzIHRvIGdldCBzcGFjZSArIGl0ZW0gSURcblx0XHRjb25zdCBtZXRhID0gdGhpcy5wbHVnaW4ubGFzdE1ldGFkYXRhO1xuXHRcdGlmICghbWV0YT8uc3BhY2UgfHwgIW1ldGE/LmlkKSB7XG5cdFx0XHRjb250ZW50RWwuY3JlYXRlRWwoXCJwXCIsIHtcblx0XHRcdFx0dGV4dDogXCJOb3QgYSBzdWJsaW1hdGVkIGZpbGUuXCIsXG5cdFx0XHRcdGNsczogXCJzYmwtdmVyc2lvbi1lbXB0eVwiLFxuXHRcdFx0fSk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Y29udGVudEVsLmNyZWF0ZUVsKFwiaDRcIiwge1xuXHRcdFx0dGV4dDogYWN0aXZlRmlsZS5uYW1lLFxuXHRcdFx0Y2xzOiBcInNibC12ZXJzaW9uLWZpbGVuYW1lXCIsXG5cdFx0fSk7XG5cblx0XHRjb25zdCBsb2FkaW5nID0gY29udGVudEVsLmNyZWF0ZUVsKFwicFwiLCB7XG5cdFx0XHR0ZXh0OiBcIkxvYWRpbmcgdmVyc2lvbnMuLi5cIixcblx0XHRcdGNsczogXCJzYmwtdmVyc2lvbi1sb2FkaW5nXCIsXG5cdFx0fSk7XG5cblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgYXBpID0gbmV3IFN1YmxpbWF0ZWRBcGkodGhpcy5wbHVnaW4uc2V0dGluZ3MuYXBpVG9rZW4pO1xuXHRcdFx0Y29uc3QgdmVyc2lvbnMgPSBhd2FpdCBhcGkuZ2V0VmVyc2lvbnMobWV0YS5zcGFjZSwgbWV0YS5pZCk7XG5cdFx0XHRsb2FkaW5nLnJlbW92ZSgpO1xuXG5cdFx0XHRpZiAodmVyc2lvbnMubGVuZ3RoID09PSAwKSB7XG5cdFx0XHRcdGNvbnRlbnRFbC5jcmVhdGVFbChcInBcIiwge1xuXHRcdFx0XHRcdHRleHQ6IFwiTm8gdmVyc2lvbnMgZm91bmQuXCIsXG5cdFx0XHRcdFx0Y2xzOiBcInNibC12ZXJzaW9uLWVtcHR5XCIsXG5cdFx0XHRcdH0pO1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdGNvbnN0IGxpc3QgPSBjb250ZW50RWwuY3JlYXRlRWwoXCJkaXZcIiwge1xuXHRcdFx0XHRjbHM6IFwic2JsLXZlcnNpb24tbGlzdFwiLFxuXHRcdFx0fSk7XG5cdFx0XHRmb3IgKGNvbnN0IHYgb2YgdmVyc2lvbnMpIHtcblx0XHRcdFx0dGhpcy5yZW5kZXJWZXJzaW9uKGxpc3QsIHYsIG1ldGEudXJsKTtcblx0XHRcdH1cblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRsb2FkaW5nLnJlbW92ZSgpO1xuXHRcdFx0Y29udGVudEVsLmNyZWF0ZUVsKFwicFwiLCB7XG5cdFx0XHRcdHRleHQ6IGBFcnJvcjogJHtlIGluc3RhbmNlb2YgRXJyb3IgPyBlLm1lc3NhZ2UgOiBcIlVua25vd25cIn1gLFxuXHRcdFx0XHRjbHM6IFwic2JsLXZlcnNpb24tZXJyb3JcIixcblx0XHRcdH0pO1xuXHRcdH1cblx0fVxuXG5cdHByaXZhdGUgcmVuZGVyVmVyc2lvbihcblx0XHRjb250YWluZXI6IEhUTUxFbGVtZW50LFxuXHRcdHY6IFZlcnNpb24sXG5cdFx0ZmlsZVVybDogc3RyaW5nIHwgbnVsbFxuXHQpOiB2b2lkIHtcblx0XHRjb25zdCBpdGVtID0gY29udGFpbmVyLmNyZWF0ZURpdih7IGNsczogXCJzYmwtdmVyc2lvbi1pdGVtXCIgfSk7XG5cblx0XHRjb25zdCBoZWFkZXIgPSBpdGVtLmNyZWF0ZURpdih7IGNsczogXCJzYmwtdmVyc2lvbi1oZWFkZXJcIiB9KTtcblx0XHRoZWFkZXIuY3JlYXRlRWwoXCJzcGFuXCIsIHtcblx0XHRcdHRleHQ6IGB2JHt2Lm51bWJlcn1gLFxuXHRcdFx0Y2xzOiBcInNibC12ZXJzaW9uLW51bWJlclwiLFxuXHRcdH0pO1xuXHRcdGhlYWRlci5jcmVhdGVFbChcInNwYW5cIiwge1xuXHRcdFx0dGV4dDogdi5hdXRob3JOYW1lLFxuXHRcdFx0Y2xzOiBcInNibC12ZXJzaW9uLWF1dGhvclwiLFxuXHRcdH0pO1xuXG5cdFx0Y29uc3QgdGltZSA9IGl0ZW0uY3JlYXRlRGl2KHsgY2xzOiBcInNibC12ZXJzaW9uLXRpbWVcIiB9KTtcblx0XHR0aW1lLmNyZWF0ZUVsKFwic3BhblwiLCB7XG5cdFx0XHR0ZXh0OiBmb3JtYXRSZWxhdGl2ZVRpbWUodi5jcmVhdGVkQXQpLFxuXHRcdFx0YXR0cjogeyB0aXRsZTogbmV3IERhdGUodi5jcmVhdGVkQXQpLnRvTG9jYWxlU3RyaW5nKCkgfSxcblx0XHR9KTtcblxuXHRcdGlmICh2Lm1lc3NhZ2UpIHtcblx0XHRcdGl0ZW0uY3JlYXRlRGl2KHtcblx0XHRcdFx0dGV4dDogdi5tZXNzYWdlLFxuXHRcdFx0XHRjbHM6IFwic2JsLXZlcnNpb24tbWVzc2FnZVwiLFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0aWYgKGZpbGVVcmwpIHtcblx0XHRcdGNvbnN0IHdlYlVybCA9IGBodHRwczovLyR7ZmlsZVVybH0/dj0ke3YubnVtYmVyfWA7XG5cdFx0XHRjb25zdCBsaW5rID0gaXRlbS5jcmVhdGVFbChcImFcIiwge1xuXHRcdFx0XHR0ZXh0OiBcInZpZXcgb24gd2ViXCIsXG5cdFx0XHRcdGNsczogXCJzYmwtdmVyc2lvbi13ZWItbGlua1wiLFxuXHRcdFx0XHRocmVmOiB3ZWJVcmwsXG5cdFx0XHR9KTtcblx0XHRcdGxpbmsuYWRkRXZlbnRMaXN0ZW5lcihcImNsaWNrXCIsIChlKSA9PiB7XG5cdFx0XHRcdGUucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0d2luZG93Lm9wZW4od2ViVXJsKTtcblx0XHRcdH0pO1xuXHRcdH1cblx0fVxuXG5cdGFzeW5jIG9uQ2xvc2UoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0Ly8gY2xlYW51cCBoYW5kbGVkIGJ5IG9ic2lkaWFuXG5cdH1cbn1cblxuZnVuY3Rpb24gZm9ybWF0UmVsYXRpdmVUaW1lKGlzbzogc3RyaW5nKTogc3RyaW5nIHtcblx0Y29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcblx0Y29uc3QgdGhlbiA9IG5ldyBEYXRlKGlzbykuZ2V0VGltZSgpO1xuXHRjb25zdCBkaWZmID0gbm93IC0gdGhlbjtcblx0Y29uc3Qgc2Vjb25kcyA9IE1hdGguZmxvb3IoZGlmZiAvIDEwMDApO1xuXHRjb25zdCBtaW51dGVzID0gTWF0aC5mbG9vcihzZWNvbmRzIC8gNjApO1xuXHRjb25zdCBob3VycyA9IE1hdGguZmxvb3IobWludXRlcyAvIDYwKTtcblx0Y29uc3QgZGF5cyA9IE1hdGguZmxvb3IoaG91cnMgLyAyNCk7XG5cblx0aWYgKGRheXMgPiAzMCkgcmV0dXJuIG5ldyBEYXRlKGlzbykudG9Mb2NhbGVEYXRlU3RyaW5nKCk7XG5cdGlmIChkYXlzID4gMCkgcmV0dXJuIGAke2RheXN9ZCBhZ29gO1xuXHRpZiAoaG91cnMgPiAwKSByZXR1cm4gYCR7aG91cnN9aCBhZ29gO1xuXHRpZiAobWludXRlcyA+IDApIHJldHVybiBgJHttaW51dGVzfW0gYWdvYDtcblx0cmV0dXJuIFwianVzdCBub3dcIjtcbn1cbiIsICJpbXBvcnQgeyBBcHAsIE1vZGFsIH0gZnJvbSBcIm9ic2lkaWFuXCI7XG5cbmV4cG9ydCBjbGFzcyBXZWxjb21lTW9kYWwgZXh0ZW5kcyBNb2RhbCB7XG5cdGNvbnN0cnVjdG9yKGFwcDogQXBwKSB7XG5cdFx0c3VwZXIoYXBwKTtcblx0fVxuXG5cdG9uT3BlbigpOiB2b2lkIHtcblx0XHRjb25zdCB7IGNvbnRlbnRFbCB9ID0gdGhpcztcblx0XHRjb250ZW50RWwuYWRkQ2xhc3MoXCJzYmwtd2VsY29tZS1tb2RhbFwiKTtcblxuXHRcdGNvbnRlbnRFbC5jcmVhdGVFbChcImgyXCIsIHsgdGV4dDogXCJXZWxjb21lIHRvIHN1YmxpbWF0ZWRcIiB9KTtcblxuXHRcdGNvbnRlbnRFbC5jcmVhdGVFbChcInBcIiwge1xuXHRcdFx0dGV4dDogXCJzdWJsaW1hdGVkIHN5bmNzIHlvdXIgZmlsZXMgd2l0aCB2ZXJzaW9uIGhpc3RvcnksIGNvbGxhYm9yYXRpb24sIGFuZCBhIHdlYiB2aWV3ZXIgXHUyMDE0IGFsbCBwb3dlcmVkIGJ5IGdpdCB1bmRlciB0aGUgaG9vZC5cIixcblx0XHR9KTtcblxuXHRcdGNvbnN0IHN0ZXBzID0gY29udGVudEVsLmNyZWF0ZUVsKFwiZGl2XCIsIHsgY2xzOiBcInNibC13ZWxjb21lLXN0ZXBzXCIgfSk7XG5cblx0XHRjb25zdCBzdGVwMSA9IHN0ZXBzLmNyZWF0ZURpdih7IGNsczogXCJzYmwtd2VsY29tZS1zdGVwXCIgfSk7XG5cdFx0c3RlcDEuY3JlYXRlRWwoXCJzdHJvbmdcIiwgeyB0ZXh0OiBcIjEuIE9wZW4geW91ciB2YXVsdCBmcm9tIHN1YmxpbWF0ZWRcIiB9KTtcblx0XHRzdGVwMS5jcmVhdGVFbChcInBcIiwge1xuXHRcdFx0dGV4dDogXCJJbiBGaW5kZXIsIG9wZW4geW91ciBzdWJsaW1hdGVkIGZvbGRlciAodW5kZXIgQ2xvdWRTdG9yYWdlKSBhcyBhbiBPYnNpZGlhbiB2YXVsdC4gVGhlIHBsdWdpbiBkZXRlY3RzIHRoZSAuc2JsLyBmb2xkZXIgYXV0b21hdGljYWxseS5cIixcblx0XHR9KTtcblxuXHRcdGNvbnN0IHN0ZXAyID0gc3RlcHMuY3JlYXRlRGl2KHsgY2xzOiBcInNibC13ZWxjb21lLXN0ZXBcIiB9KTtcblx0XHRzdGVwMi5jcmVhdGVFbChcInN0cm9uZ1wiLCB7IHRleHQ6IFwiMi4gU3RhdHVzIGJhciBzaG93cyBzeW5jIGluZm9cIiB9KTtcblx0XHRzdGVwMi5jcmVhdGVFbChcInBcIiwge1xuXHRcdFx0dGV4dDogXCJUaGUgYm90dG9tIGJhciBzaG93cyB2ZXJzaW9uIG51bWJlciwgc3luYyBzdGF0dXMsIGFuZCBhdXRob3IgZm9yIHRoZSBjdXJyZW50IGZpbGUgXHUyMDE0IGFsbCByZWFkIGZyb20gbG9jYWwgZmlsZSBtZXRhZGF0YSwgbm8gYWNjb3VudCBuZWVkZWQuXCIsXG5cdFx0fSk7XG5cblx0XHRjb25zdCBzdGVwMyA9IHN0ZXBzLmNyZWF0ZURpdih7IGNsczogXCJzYmwtd2VsY29tZS1zdGVwXCIgfSk7XG5cdFx0c3RlcDMuY3JlYXRlRWwoXCJzdHJvbmdcIiwgeyB0ZXh0OiBcIjMuIEFkZCBhbiBBUEkgdG9rZW4gZm9yIHZlcnNpb24gaGlzdG9yeVwiIH0pO1xuXHRcdHN0ZXAzLmNyZWF0ZUVsKFwicFwiLCB7XG5cdFx0XHR0ZXh0OiBcIlRvIGJyb3dzZSBwYXN0IHZlcnNpb25zLCBhZGQgeW91ciBBUEkgdG9rZW4gaW4gU2V0dGluZ3MgXHUyMTkyIHN1YmxpbWF0ZWQuIEdlbmVyYXRlIG9uZSBhdCBzdWJsaW1hdGVkLmNvbS9zZXR0aW5ncy90b2tlbnMuXCIsXG5cdFx0fSk7XG5cblx0XHRjb25zdCBsaW5rcyA9IGNvbnRlbnRFbC5jcmVhdGVEaXYoeyBjbHM6IFwic2JsLXdlbGNvbWUtbGlua3NcIiB9KTtcblx0XHRsaW5rcy5jcmVhdGVFbChcImFcIiwge1xuXHRcdFx0dGV4dDogXCJMZWFybiBtb3JlIGF0IHN1YmxpbWF0ZWQuY29tXCIsXG5cdFx0XHRocmVmOiBcImh0dHBzOi8vc3VibGltYXRlZC5jb21cIixcblx0XHR9KTtcblx0XHRsaW5rcy5jcmVhdGVFbChcImJyXCIpO1xuXHRcdGxpbmtzLmNyZWF0ZUVsKFwiYVwiLCB7XG5cdFx0XHR0ZXh0OiBcIkRvY3VtZW50YXRpb25cIixcblx0XHRcdGhyZWY6IFwiaHR0cHM6Ly9zdWJsaW1hdGVkLmNvbS9kb2NzL29ic2lkaWFuXCIsXG5cdFx0fSk7XG5cblx0XHRjb25zdCBmb290ZXIgPSBjb250ZW50RWwuY3JlYXRlRGl2KHsgY2xzOiBcInNibC13ZWxjb21lLWZvb3RlclwiIH0pO1xuXHRcdGZvb3Rlci5jcmVhdGVFbChcImJ1dHRvblwiLCB7XG5cdFx0XHR0ZXh0OiBcIkdldCBzdGFydGVkXCIsXG5cdFx0XHRjbHM6IFwibW9kLWN0YVwiLFxuXHRcdH0pLmFkZEV2ZW50TGlzdGVuZXIoXCJjbGlja1wiLCAoKSA9PiB0aGlzLmNsb3NlKCkpO1xuXHR9XG5cblx0b25DbG9zZSgpOiB2b2lkIHtcblx0XHR0aGlzLmNvbnRlbnRFbC5lbXB0eSgpO1xuXHR9XG59XG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLElBQUFBLG1CQUtPOzs7QUNMUCxJQUFBQyxtQkFBdUQ7OztBQ0F2RCxzQkFBMkI7QUFFM0IsSUFBTSxXQUFXO0FBc0JWLElBQU0sZ0JBQU4sTUFBb0I7QUFBQSxFQUMxQixZQUFvQixPQUFlO0FBQWY7QUFBQSxFQUFnQjtBQUFBLEVBRXBDLE1BQWMsUUFBVyxNQUEwQjtBQUNsRCxVQUFNLFdBQVcsVUFBTSw0QkFBVztBQUFBLE1BQ2pDLEtBQUssR0FBRyxRQUFRLEdBQUcsSUFBSTtBQUFBLE1BQ3ZCLFNBQVM7QUFBQSxRQUNSLGVBQWUsVUFBVSxLQUFLLEtBQUs7QUFBQSxRQUNuQyxRQUFRO0FBQUEsTUFDVDtBQUFBLElBQ0QsQ0FBQztBQUVELFFBQUksU0FBUyxVQUFVLEtBQUs7QUFDM0IsWUFBTSxJQUFJO0FBQUEsUUFDVCxhQUFhLFNBQVMsTUFBTSxL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QSxRQUFJLEtBQUssS0FBSztBQUNiLFdBQUssWUFBWSxTQUFTLGVBQWU7QUFBQSxJQUMxQyxPQUFPO0FBQ04sV0FBSyxZQUFZLFlBQVksZUFBZTtBQUFBLElBQzdDO0FBQUEsRUFDRDtBQUFBLEVBRVEsdUJBQTZCO0FBQ3BDLFVBQU0sTUFBTSxLQUFLLGNBQWM7QUFDL0IsUUFBSSxDQUFDLEtBQUs7QUFDVCxVQUFJLHdCQUFPLGlDQUFpQztBQUM1QztBQUFBLElBQ0Q7QUFDQSxXQUFPLEtBQUssV0FBVyxHQUFHLEVBQUU7QUFBQSxFQUM3QjtBQUFBLEVBRUEsTUFBYyxjQUE2QjtBQUMxQyxVQUFNLE1BQU0sS0FBSyxjQUFjO0FBQy9CLFFBQUksQ0FBQyxLQUFLO0FBQ1QsVUFBSSx3QkFBTyxpQ0FBaUM7QUFDNUM7QUFBQSxJQUNEO0FBQ0EsVUFBTSxVQUFVLFVBQVUsVUFBVSxXQUFXLEdBQUcsRUFBRTtBQUNwRCxRQUFJLHdCQUFPLDBCQUEwQjtBQUFBLEVBQ3RDO0FBQUEsRUFFQSxNQUFjLHNCQUFxQztBQUNsRCxVQUFNLFdBQ0wsS0FBSyxJQUFJLFVBQVUsZ0JBQWdCLGlCQUFpQjtBQUNyRCxRQUFJLFNBQVMsU0FBUyxHQUFHO0FBQ3hCLFdBQUssSUFBSSxVQUFVLFdBQVcsU0FBUyxDQUFDLENBQUM7QUFDekM7QUFBQSxJQUNEO0FBRUEsVUFBTSxPQUFPLEtBQUssSUFBSSxVQUFVLGFBQWEsS0FBSztBQUNsRCxRQUFJLENBQUMsS0FBTTtBQUNYLFVBQU0sS0FBSyxhQUFhO0FBQUEsTUFDdkIsTUFBTTtBQUFBLE1BQ04sUUFBUTtBQUFBLElBQ1QsQ0FBQztBQUNELFNBQUssSUFBSSxVQUFVLFdBQVcsSUFBSTtBQUFBLEVBQ25DO0FBQUEsRUFFUSxVQUFVLFFBQXNDO0FBQ3ZELFVBQU0sRUFBRSxRQUFRLEtBQUssSUFBSTtBQUV6QixRQUFJLFdBQVcsVUFBVSxNQUFNO0FBQzlCLFlBQU0saUJBQWEsZ0NBQWMsSUFBSTtBQUNyQyxZQUFNLFdBQVcsS0FBSyxJQUFJLE1BQU0sc0JBQXNCLFVBQVU7QUFDaEUsVUFBSSxVQUFVO0FBQ2IsYUFBSyxJQUFJLFVBQVUsYUFBYSxZQUFZLElBQUksS0FBSztBQUFBLE1BQ3RELE9BQU87QUFDTixZQUFJLHdCQUFPLG1CQUFtQixJQUFJLEVBQUU7QUFBQSxNQUNyQztBQUNBO0FBQUEsSUFDRDtBQUVBLFFBQUksV0FBVyxVQUFVLE9BQU8sT0FBTztBQUV0QyxXQUFLLFNBQVMsV0FBVyxPQUFPO0FBQ2hDLFdBQUssYUFBYTtBQUNsQixVQUFJLHdCQUFPLG9DQUFvQztBQUMvQztBQUFBLElBQ0Q7QUFFQSxRQUFJLHdCQUFPLDhCQUE4QixNQUFNLEVBQUU7QUFBQSxFQUNsRDtBQUNEOyIsCiAgIm5hbWVzIjogWyJpbXBvcnRfb2JzaWRpYW4iLCAiaW1wb3J0X29ic2lkaWFuIiwgImltcG9ydF9vYnNpZGlhbiIsICJpbXBvcnRfb2JzaWRpYW4iLCAiaW1wb3J0X29ic2lkaWFuIl0KfQo=A /sublimated/undefined +656 -0
1{2 "name": "obsidian-sublimated",3 "version": "0.1.0",4 "lockfileVersion": 3,5 "requires": true,6 "packages": {7 "": {8 "name": "obsidian-sublimated",9 "version": "0.1.0",10 "devDependencies": {11 "@types/node": "^22.0.0",12 "builtin-modules": "^4.0.0",13 "esbuild": "^0.25.0",14 "obsidian": "^1.7.2",15 "typescript": "^5.7.0"16 }17 },18 "node_modules/@codemirror/state": {19 "version": "6.5.0",20 "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz",21 "integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",22 "dev": true,23 "license": "MIT",24 "peer": true,25 "dependencies": {26 "@marijn/find-cluster-break": "^1.0.0"27 }28 },29 "node_modules/@codemirror/view": {30 "version": "6.38.6",31 "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz",32 "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==",33 "dev": true,34 "license": "MIT",35 "peer": true,36 "dependencies": {37 "@codemirror/state": "^6.5.0",38 "crelt": "^1.0.6",39 "style-mod": "^4.1.0",40 "w3c-keyname": "^2.2.4"41 }42 },43 "node_modules/@esbuild/aix-ppc64": {44 "version": "0.25.12",45 "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",46 "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",47 "cpu": [48 "ppc64"49 ],50 "dev": true,51 "license": "MIT",52 "optional": true,53 "os": [54 "aix"55 ],56 "engines": {57 "node": ">=18"58 }59 },60 "node_modules/@esbuild/android-arm": {61 "version": "0.25.12",62 "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",63 "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",64 "cpu": [65 "arm"66 ],67 "dev": true,68 "license": "MIT",69 "optional": true,70 "os": [71 "android"72 ],73 "engines": {74 "node": ">=18"75 }76 },77 "node_modules/@esbuild/android-arm64": {78 "version": "0.25.12",79 "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",80 "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",81 "cpu": [82 "arm64"83 ],84 "dev": true,85 "license": "MIT",86 "optional": true,87 "os": [88 "android"89 ],90 "engines": {91 "node": ">=18"92 }93 },94 "node_modules/@esbuild/android-x64": {95 "version": "0.25.12",96 "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",97 "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",98 "cpu": [99 "x64"100 ],101 "dev": true,102 "license": "MIT",103 "optional": true,104 "os": [105 "android"106 ],107 "engines": {108 "node": ">=18"109 }110 },111 "node_modules/@esbuild/darwin-arm64": {112 "version": "0.25.12",113 "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",114 "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",115 "cpu": [116 "arm64"117 ],118 "dev": true,119 "license": "MIT",120 "optional": true,121 "os": [122 "darwin"123 ],124 "engines": {125 "node": ">=18"126 }127 },128 "node_modules/@esbuild/darwin-x64": {129 "version": "0.25.12",130 "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",131 "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",132 "cpu": [133 "x64"134 ],135 "dev": true,136 "license": "MIT",137 "optional": true,138 "os": [139 "darwin"140 ],141 "engines": {142 "node": ">=18"143 }144 },145 "node_modules/@esbuild/freebsd-arm64": {146 "version": "0.25.12",147 "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",148 "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",149 "cpu": [150 "arm64"151 ],152 "dev": true,153 "license": "MIT",154 "optional": true,155 "os": [156 "freebsd"157 ],158 "engines": {159 "node": ">=18"160 }161 },162 "node_modules/@esbuild/freebsd-x64": {163 "version": "0.25.12",164 "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",165 "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",166 "cpu": [167 "x64"168 ],169 "dev": true,170 "license": "MIT",171 "optional": true,172 "os": [173 "freebsd"174 ],175 "engines": {176 "node": ">=18"177 }178 },179 "node_modules/@esbuild/linux-arm": {180 "version": "0.25.12",181 "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",182 "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",183 "cpu": [184 "arm"185 ],186 "dev": true,187 "license": "MIT",188 "optional": true,189 "os": [190 "linux"191 ],192 "engines": {193 "node": ">=18"194 }195 },196 "node_modules/@esbuild/linux-arm64": {197 "version": "0.25.12",198 "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",199 "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",200 "cpu": [201 "arm64"202 ],203 "dev": true,204 "license": "MIT",205 "optional": true,206 "os": [207 "linux"208 ],209 "engines": {210 "node": ">=18"211 }212 },213 "node_modules/@esbuild/linux-ia32": {214 "version": "0.25.12",215 "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",216 "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",217 "cpu": [218 "ia32"219 ],220 "dev": true,221 "license": "MIT",222 "optional": true,223 "os": [224 "linux"225 ],226 "engines": {227 "node": ">=18"228 }229 },230 "node_modules/@esbuild/linux-loong64": {231 "version": "0.25.12",232 "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",233 "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",234 "cpu": [235 "loong64"236 ],237 "dev": true,238 "license": "MIT",239 "optional": true,240 "os": [241 "linux"242 ],243 "engines": {244 "node": ">=18"245 }246 },247 "node_modules/@esbuild/linux-mips64el": {248 "version": "0.25.12",249 "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",250 "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",251 "cpu": [252 "mips64el"253 ],254 "dev": true,255 "license": "MIT",256 "optional": true,257 "os": [258 "linux"259 ],260 "engines": {261 "node": ">=18"262 }263 },264 "node_modules/@esbuild/linux-ppc64": {265 "version": "0.25.12",266 "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",267 "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",268 "cpu": [269 "ppc64"270 ],271 "dev": true,272 "license": "MIT",273 "optional": true,274 "os": [275 "linux"276 ],277 "engines": {278 "node": ">=18"279 }280 },281 "node_modules/@esbuild/linux-riscv64": {282 "version": "0.25.12",283 "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",284 "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",285 "cpu": [286 "riscv64"287 ],288 "dev": true,289 "license": "MIT",290 "optional": true,291 "os": [292 "linux"293 ],294 "engines": {295 "node": ">=18"296 }297 },298 "node_modules/@esbuild/linux-s390x": {299 "version": "0.25.12",300 "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",301 "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",302 "cpu": [303 "s390x"304 ],305 "dev": true,306 "license": "MIT",307 "optional": true,308 "os": [309 "linux"310 ],311 "engines": {312 "node": ">=18"313 }314 },315 "node_modules/@esbuild/linux-x64": {316 "version": "0.25.12",317 "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",318 "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",319 "cpu": [320 "x64"321 ],322 "dev": true,323 "license": "MIT",324 "optional": true,325 "os": [326 "linux"327 ],328 "engines": {329 "node": ">=18"330 }331 },332 "node_modules/@esbuild/netbsd-arm64": {333 "version": "0.25.12",334 "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",335 "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",336 "cpu": [337 "arm64"338 ],339 "dev": true,340 "license": "MIT",341 "optional": true,342 "os": [343 "netbsd"344 ],345 "engines": {346 "node": ">=18"347 }348 },349 "node_modules/@esbuild/netbsd-x64": {350 "version": "0.25.12",351 "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",352 "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",353 "cpu": [354 "x64"355 ],356 "dev": true,357 "license": "MIT",358 "optional": true,359 "os": [360 "netbsd"361 ],362 "engines": {363 "node": ">=18"364 }365 },366 "node_modules/@esbuild/openbsd-arm64": {367 "version": "0.25.12",368 "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",369 "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",370 "cpu": [371 "arm64"372 ],373 "dev": true,374 "license": "MIT",375 "optional": true,376 "os": [377 "openbsd"378 ],379 "engines": {380 "node": ">=18"381 }382 },383 "node_modules/@esbuild/openbsd-x64": {384 "version": "0.25.12",385 "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",386 "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",387 "cpu": [388 "x64"389 ],390 "dev": true,391 "license": "MIT",392 "optional": true,393 "os": [394 "openbsd"395 ],396 "engines": {397 "node": ">=18"398 }399 },400 "node_modules/@esbuild/openharmony-arm64": {401 "version": "0.25.12",402 "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",403 "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",404 "cpu": [405 "arm64"406 ],407 "dev": true,408 "license": "MIT",409 "optional": true,410 "os": [411 "openharmony"412 ],413 "engines": {414 "node": ">=18"415 }416 },417 "node_modules/@esbuild/sunos-x64": {418 "version": "0.25.12",419 "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",420 "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",421 "cpu": [422 "x64"423 ],424 "dev": true,425 "license": "MIT",426 "optional": true,427 "os": [428 "sunos"429 ],430 "engines": {431 "node": ">=18"432 }433 },434 "node_modules/@esbuild/win32-arm64": {435 "version": "0.25.12",436 "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",437 "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",438 "cpu": [439 "arm64"440 ],441 "dev": true,442 "license": "MIT",443 "optional": true,444 "os": [445 "win32"446 ],447 "engines": {448 "node": ">=18"449 }450 },451 "node_modules/@esbuild/win32-ia32": {452 "version": "0.25.12",453 "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",454 "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",455 "cpu": [456 "ia32"457 ],458 "dev": true,459 "license": "MIT",460 "optional": true,461 "os": [462 "win32"463 ],464 "engines": {465 "node": ">=18"466 }467 },468 "node_modules/@esbuild/win32-x64": {469 "version": "0.25.12",470 "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",471 "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",472 "cpu": [473 "x64"474 ],475 "dev": true,476 "license": "MIT",477 "optional": true,478 "os": [479 "win32"480 ],481 "engines": {482 "node": ">=18"483 }484 },485 "node_modules/@marijn/find-cluster-break": {486 "version": "1.0.2",487 "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",488 "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",489 "dev": true,490 "license": "MIT",491 "peer": true492 },493 "node_modules/@types/codemirror": {494 "version": "5.60.8",495 "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz",496 "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",497 "dev": true,498 "license": "MIT",499 "dependencies": {500 "@types/tern": "*"501 }502 },503 "node_modules/@types/estree": {504 "version": "1.0.8",505 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",506 "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",507 "dev": true,508 "license": "MIT"509 },510 "node_modules/@types/node": {511 "version": "22.19.15",512 "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz",513 "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==",514 "dev": true,515 "license": "MIT",516 "dependencies": {517 "undici-types": "~6.21.0"518 }519 },520 "node_modules/@types/tern": {521 "version": "0.23.9",522 "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",523 "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",524 "dev": true,525 "license": "MIT",526 "dependencies": {527 "@types/estree": "*"528 }529 },530 "node_modules/builtin-modules": {531 "version": "4.0.0",532 "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz",533 "integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==",534 "dev": true,535 "license": "MIT",536 "engines": {537 "node": ">=18.20"538 },539 "funding": {540 "url": "https://github.com/sponsors/sindresorhus"541 }542 },543 "node_modules/crelt": {544 "version": "1.0.6",545 "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",546 "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",547 "dev": true,548 "license": "MIT",549 "peer": true550 },551 "node_modules/esbuild": {552 "version": "0.25.12",553 "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",554 "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",555 "dev": true,556 "hasInstallScript": true,557 "license": "MIT",558 "bin": {559 "esbuild": "bin/esbuild"560 },561 "engines": {562 "node": ">=18"563 },564 "optionalDependencies": {565 "@esbuild/aix-ppc64": "0.25.12",566 "@esbuild/android-arm": "0.25.12",567 "@esbuild/android-arm64": "0.25.12",568 "@esbuild/android-x64": "0.25.12",569 "@esbuild/darwin-arm64": "0.25.12",570 "@esbuild/darwin-x64": "0.25.12",571 "@esbuild/freebsd-arm64": "0.25.12",572 "@esbuild/freebsd-x64": "0.25.12",573 "@esbuild/linux-arm": "0.25.12",574 "@esbuild/linux-arm64": "0.25.12",575 "@esbuild/linux-ia32": "0.25.12",576 "@esbuild/linux-loong64": "0.25.12",577 "@esbuild/linux-mips64el": "0.25.12",578 "@esbuild/linux-ppc64": "0.25.12",579 "@esbuild/linux-riscv64": "0.25.12",580 "@esbuild/linux-s390x": "0.25.12",581 "@esbuild/linux-x64": "0.25.12",582 "@esbuild/netbsd-arm64": "0.25.12",583 "@esbuild/netbsd-x64": "0.25.12",584 "@esbuild/openbsd-arm64": "0.25.12",585 "@esbuild/openbsd-x64": "0.25.12",586 "@esbuild/openharmony-arm64": "0.25.12",587 "@esbuild/sunos-x64": "0.25.12",588 "@esbuild/win32-arm64": "0.25.12",589 "@esbuild/win32-ia32": "0.25.12",590 "@esbuild/win32-x64": "0.25.12"591 }592 },593 "node_modules/moment": {594 "version": "2.29.4",595 "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",596 "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",597 "dev": true,598 "license": "MIT",599 "engines": {600 "node": "*"601 }602 },603 "node_modules/obsidian": {604 "version": "1.12.3",605 "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz",606 "integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==",607 "dev": true,608 "license": "MIT",609 "dependencies": {610 "@types/codemirror": "5.60.8",611 "moment": "2.29.4"612 },613 "peerDependencies": {614 "@codemirror/state": "6.5.0",615 "@codemirror/view": "6.38.6"616 }617 },618 "node_modules/style-mod": {619 "version": "4.1.3",620 "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",621 "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",622 "dev": true,623 "license": "MIT",624 "peer": true625 },626 "node_modules/typescript": {627 "version": "5.9.3",628 "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",629 "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",630 "dev": true,631 "license": "Apache-2.0",632 "bin": {633 "tsc": "bin/tsc",634 "tsserver": "bin/tsserver"635 },636 "engines": {637 "node": ">=14.17"638 }639 },640 "node_modules/undici-types": {641 "version": "6.21.0",642 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",643 "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",644 "dev": true,645 "license": "MIT"646 },647 "node_modules/w3c-keyname": {648 "version": "2.2.8",649 "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",650 "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",651 "dev": true,652 "license": "MIT",653 "peer": true654 }655 }656}A /sublimated/undefined +61 -0
1# sublimated for Obsidian2
3Sync status, version history, and deep links for [sublimated.com](https://sublimated.com) vaults.4
5## features6
7- status bar showing version, sync status, and author for the current file8- click status bar to open the file on sublimated.com9- version history sidebar with author, timestamp, and web links10- commands: open on web, show versions, copy web link11- URI handler for deep linking from sublimated.com back to Obsidian12- works without an account for basic features (reads local file metadata)13
14## setup15
161. install the plugin from Obsidian community plugins172. open your sublimated folder (Finder → CloudStorage → Sublimated) as an Obsidian vault183. the plugin detects the `.sbl/` folder and starts showing sync info in the status bar19
20## version history21
22version history requires an API token:23
241. go to sublimated.com/settings/tokens and generate a token252. in Obsidian, go to Settings → sublimated → paste the token263. use "sublimated: show versions" command or click the ribbon icon27
28## authentication29
30**current:** API token (paste in settings)31
32**planned:** OAuth sign-in — click "Sign in with sublimated" in settings, authenticate in browser, token flows back automatically via `obsidian://sublimated?action=auth&token=...`33
34## commands35
36- `sublimated: open on sublimated.com` — open current file in browser37- `sublimated: show versions` — open version history sidebar38- `sublimated: copy web link` — copy sublimated.com URL to clipboard39
40## URI handler41
42```43obsidian://sublimated?action=open&file=path/to/note.md44```45
46opens the specified file in your vault. used by sublimated.com's "Open in Obsidian" feature.47
48## platform support49
50- macOS: full features (xattr-based status bar + API features)51- Windows/Linux: API-only mode (version history works, status bar requires API token)52
53## development54
55```bash56npm install57npm run build # one-time build58npm run dev # watch mode59```60
61copy `main.js`, `manifest.json`, and `styles.css` to your vault's `.obsidian/plugins/sublimated/` folder.A /sublimated/undefined +10 -0
1{2 "id": "sublimated",3 "name": "Sublimated",4 "version": "0.1.0",5 "minAppVersion": "1.5.0",6 "description": "Sync status, version history, and deep links for sublimated.com vaults.",7 "author": "sublimated",8 "authorUrl": "https://sublimated.com",9 "isDesktopOnly": true10}A /sublimated/undefined +2 -0
1node_modules/2main.jsA /sublimated/undefined +134 -0
1/* status bar */2.sbl-status-bar {3 cursor: default;4 opacity: 0.8;5}6
7.sbl-status-bar.sbl-clickable {8 cursor: pointer;9}10
11.sbl-status-bar.sbl-clickable:hover {12 opacity: 1;13}14
15/* version view */16.sbl-version-view {17 padding: 8px 12px;18}19
20.sbl-version-empty,21.sbl-version-hint {22 color: var(--text-muted);23 font-size: var(--font-ui-small);24}25
26.sbl-version-loading {27 color: var(--text-muted);28 font-style: italic;29}30
31.sbl-version-error {32 color: var(--text-error);33 font-size: var(--font-ui-small);34}35
36.sbl-version-filename {37 margin: 0 0 8px 0;38 font-size: var(--font-ui-medium);39 font-weight: 600;40}41
42.sbl-version-list {43 display: flex;44 flex-direction: column;45 gap: 4px;46}47
48.sbl-version-item {49 padding: 6px 8px;50 border-radius: 4px;51 border: 1px solid var(--background-modifier-border);52}53
54.sbl-version-item:hover {55 background: var(--background-modifier-hover);56}57
58.sbl-version-header {59 display: flex;60 align-items: center;61 gap: 8px;62}63
64.sbl-version-number {65 font-weight: 600;66 font-size: var(--font-ui-small);67 color: var(--text-accent);68}69
70.sbl-version-author {71 font-size: var(--font-ui-small);72 color: var(--text-muted);73}74
75.sbl-version-time {76 font-size: var(--font-ui-smaller);77 color: var(--text-faint);78 margin-top: 2px;79}80
81.sbl-version-message {82 font-size: var(--font-ui-small);83 color: var(--text-normal);84 margin-top: 4px;85}86
87.sbl-version-web-link {88 font-size: var(--font-ui-smaller);89 margin-top: 4px;90 display: inline-block;91}92
93/* welcome modal */94.sbl-welcome-modal {95 max-width: 480px;96}97
98.sbl-welcome-steps {99 margin: 16px 0;100}101
102.sbl-welcome-step {103 margin-bottom: 12px;104}105
106.sbl-welcome-step p {107 margin: 4px 0 0 0;108 color: var(--text-muted);109 font-size: var(--font-ui-small);110}111
112.sbl-welcome-links {113 margin: 16px 0;114}115
116.sbl-welcome-links a {117 font-size: var(--font-ui-small);118}119
120.sbl-welcome-footer {121 text-align: right;122 margin-top: 16px;123}124
125/* settings */126.sbl-settings-header p {127 margin-top: 4px;128}129
130.sbl-settings-links {131 margin-top: 24px;132 font-size: var(--font-ui-small);133 color: var(--text-muted);134}