f2f390b antonApr 8, 2026 api

rename to code

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 changes
30 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 ID
64 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 | null
115 ): 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 obsidian
157 }
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: string
49 ): 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 // branding
28 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 detection
36 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 token
52 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 text
59 .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 connection
68 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.apiToken
81 );
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 // links
98 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 workspace
27 this.isSublimatedVault = await this.checkSublimatedVault();
28
29 // status bar
30 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 view
38 this.registerView(
39 VERSION_VIEW_TYPE,
40 (leaf) => new VersionView(leaf, this)
41 );
42
43 // commands
44 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 sidebar
63 this.addRibbonIcon("history", "sublimated versions", () => {
64 this.activateVersionView();
65 });
66
67 // URI handler: obsidian://sublimated?action=open&file=path/to/note.md
68 this.registerObsidianProtocolHandler(
69 "sublimated",
70 (params) => this.handleUri(params)
71 );
72
73 // settings tab
74 this.addSettingTab(new SublimatedSettingTab(this.app, this));
75
76 // update status bar when active file changes
77 this.registerEvent(
78 this.app.workspace.on("active-leaf-change", () => {
79 this.refreshMetadata();
80 })
81 );
82
83 // initial metadata read
84 this.refreshMetadata();
85
86 // welcome modal on first install
87 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 root
111 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 adapter
138 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 available
177 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 redirect
236 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-hash
39 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: string
61): 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 file
69 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.ts
20var main_exports = {};
21__export(main_exports, {
22 default: () => SublimatedPlugin
23});
24module.exports = __toCommonJS(main_exports);
25var import_obsidian6 = require("obsidian");
26
27// src/settings.ts
28var import_obsidian2 = require("obsidian");
29
30// src/api.ts
31var 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.ts
74var DEFAULT_SETTINGS = {
75 apiToken: "",
76 hasSeenWelcome: false
77};
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.apiToken
121 );
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.ts
156var 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: null
212 };
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.ts
223var 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: webUrl
340 });
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.ts
366var 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.ts
416var 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 root
484 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 ?? null
492 };
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: true
570 });
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": true
492 },
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": true
550 },
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": true
625 },
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": true
654 }
655 }
656}
A /sublimated/undefined +61 -0
1# sublimated for Obsidian
2
3Sync status, version history, and deep links for [sublimated.com](https://sublimated.com) vaults.
4
5## features
6
7- status bar showing version, sync status, and author for the current file
8- click status bar to open the file on sublimated.com
9- version history sidebar with author, timestamp, and web links
10- commands: open on web, show versions, copy web link
11- URI handler for deep linking from sublimated.com back to Obsidian
12- works without an account for basic features (reads local file metadata)
13
14## setup
15
161. install the plugin from Obsidian community plugins
172. open your sublimated folder (Finder → CloudStorage → Sublimated) as an Obsidian vault
183. the plugin detects the `.sbl/` folder and starts showing sync info in the status bar
19
20## version history
21
22version history requires an API token:
23
241. go to sublimated.com/settings/tokens and generate a token
252. in Obsidian, go to Settings → sublimated → paste the token
263. use "sublimated: show versions" command or click the ribbon icon
27
28## authentication
29
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## commands
35
36- `sublimated: open on sublimated.com` — open current file in browser
37- `sublimated: show versions` — open version history sidebar
38- `sublimated: copy web link` — copy sublimated.com URL to clipboard
39
40## URI handler
41
42```
43obsidian://sublimated?action=open&file=path/to/note.md
44```
45
46opens the specified file in your vault. used by sublimated.com's "Open in Obsidian" feature.
47
48## platform support
49
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## development
54
55```bash
56npm install
57npm run build # one-time build
58npm run dev # watch mode
59```
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": true
10}
A /sublimated/undefined +2 -0
1node_modules/
2main.js
A /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}