ObsidianCustomFrames/main.ts

261 lines
8.1 KiB
TypeScript
Raw Permalink Normal View History

import { App, ButtonComponent, DropdownComponent, ItemView, Plugin, PluginSettingTab, Setting, WorkspaceLeaf, Platform } from "obsidian";
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
const defaultSettings: CustomFramesSettings = {
2022-03-22 13:17:10 +01:00
frames: [],
padding: 5
};
const presets: Record<string, CustomFrame> = {
"keep": {
url: "https://keep.google.com",
displayName: "Google Keep",
icon: "files",
2022-03-22 13:17:10 +01:00
minimumWidth: 370,
customCss: `/* hide the menu bar and the "Keep" text */
.PvRhvb-qAWA2, .gb_2d.gb_Zc {
2022-03-20 13:55:19 +01:00
display: none !important;
}`
2022-03-22 13:17:10 +01:00
}
};
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
interface CustomFramesSettings {
2022-03-22 13:17:10 +01:00
frames: CustomFrame[];
2022-03-20 13:55:19 +01:00
padding: number;
2022-03-22 13:17:10 +01:00
}
interface CustomFrame {
url: string;
displayName: string;
icon: string;
2022-03-22 13:17:10 +01:00
minimumWidth: number;
customCss: string;
2022-03-20 13:55:19 +01:00
}
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
export default class CustomFramesPlugin extends Plugin {
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
settings: CustomFramesSettings;
2022-03-19 20:21:16 +01:00
2022-03-20 00:20:09 +01:00
async onload(): Promise<void> {
2022-03-20 13:55:19 +01:00
await this.loadSettings();
2022-03-19 20:21:16 +01:00
2022-03-22 13:17:10 +01:00
for (let frame of this.settings.frames) {
if (!frame.url || !frame.displayName)
continue;
let name = `custom-frames-${frame.displayName.toLowerCase().replace(/\s/g, "-")}`;
try {
console.log(`Registering frame ${name} for URL ${frame.url}`);
this.registerView(name, l => new CustomFrameView(l, this.settings, frame, name));
this.addCommand({
id: `open-${name}`,
name: `Open ${frame.displayName}`,
callback: () => this.openLeaf(name),
2022-03-22 13:17:10 +01:00
});
} catch {
console.error(`Couldn't register frame ${name}, is there already one with the same name?`);
}
}
2022-03-19 20:21:16 +01:00
2022-03-22 13:17:10 +01:00
this.addSettingTab(new CustomFramesSettingTab(this.app, this));
2022-03-20 00:20:09 +01:00
}
2022-03-20 13:55:19 +01:00
async loadSettings() {
this.settings = Object.assign({}, defaultSettings, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
private async openLeaf(name: string): Promise<void> {
if (!this.app.workspace.getLeavesOfType(name).length)
await this.app.workspace.getRightLeaf(false).setViewState({ type: name });
this.app.workspace.revealLeaf(this.app.workspace.getLeavesOfType(name)[0]);
}
2022-03-20 00:20:09 +01:00
}
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
class CustomFrameView extends ItemView {
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
private settings: CustomFramesSettings;
2022-03-22 13:17:10 +01:00
private frame: CustomFrame;
private name: string;
2022-03-19 20:21:16 +01:00
2022-03-22 13:17:10 +01:00
constructor(leaf: WorkspaceLeaf, settings: CustomFramesSettings, frame: CustomFrame, name: string) {
2022-03-20 13:55:19 +01:00
super(leaf);
this.settings = settings;
2022-03-22 13:17:10 +01:00
this.frame = frame;
this.name = name;
2022-03-20 13:55:19 +01:00
}
onload(): void {
this.contentEl.empty();
2022-03-22 13:17:10 +01:00
this.contentEl.addClass("custom-frames-view");
2022-03-26 11:23:11 +01:00
let frame: any = document.createElement("webview");
2022-03-22 13:17:10 +01:00
frame.addClass("custom-frames-frame");
2022-03-26 11:23:11 +01:00
frame.setAttribute("allowpopups", "");
frame.setAttribute("style", `padding: ${this.settings.padding}px`);
2022-03-26 11:23:11 +01:00
frame.setAttribute("src", this.frame.url);
frame.addEventListener("dom-ready", () => {
frame.insertCSS(this.frame.customCss);
2022-03-19 20:21:16 +01:00
2022-03-26 11:23:11 +01:00
if (this.frame.minimumWidth) {
let parent = this.contentEl.closest<HTMLElement>(".workspace-split.mod-horizontal");
if (parent) {
let minWidth = `${this.frame.minimumWidth + 2 * this.settings.padding}px`;
if (parent.style.width < minWidth)
parent.style.width = minWidth;
2022-03-20 19:32:21 +01:00
}
}
2022-03-26 11:23:11 +01:00
});
this.contentEl.appendChild(frame);
2022-03-20 00:20:09 +01:00
}
2022-03-19 20:21:16 +01:00
getViewType(): string {
2022-03-22 13:17:10 +01:00
return this.name;
}
getDisplayText(): string {
2022-03-22 13:17:10 +01:00
return this.frame.displayName;
2022-03-20 00:20:09 +01:00
}
2022-03-19 20:21:16 +01:00
getIcon(): string {
return this.frame.icon ? `lucide-${this.frame.icon}` : "documents";
2022-03-19 20:21:16 +01:00
}
2022-03-20 13:55:19 +01:00
}
2022-03-22 12:03:48 +01:00
class CustomFramesSettingTab extends PluginSettingTab {
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
plugin: CustomFramesPlugin;
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
constructor(app: App, plugin: CustomFramesPlugin) {
2022-03-20 13:55:19 +01:00
super(app, plugin);
2022-03-20 13:57:02 +01:00
this.plugin = plugin;
2022-03-20 13:55:19 +01:00
}
display(): void {
this.containerEl.empty();
2022-03-22 13:17:10 +01:00
this.containerEl.createEl("h2", { text: "Custom Frames Settings" });
this.containerEl.createEl("p", { text: "Note that Obsidian has to be restarted or reloaded for most of these settings to take effect.", cls: "mod-warning" });
2022-03-20 13:55:19 +01:00
new Setting(this.containerEl)
2022-03-22 13:17:10 +01:00
.setName("Frame Padding")
.setDesc("The padding that should be left around the inside of custom frame panes, in pixels.")
2022-03-20 19:32:21 +01:00
.addText(t => {
t.inputEl.type = "number";
t.setValue(String(this.plugin.settings.padding));
t.onChange(async v => {
2022-03-21 19:39:03 +01:00
this.plugin.settings.padding = v.length ? Number(v) : defaultSettings.padding;
2022-03-20 19:32:21 +01:00
await this.plugin.saveSettings();
});
});
2022-03-22 13:17:10 +01:00
for (let frame of this.plugin.settings.frames) {
let heading = this.containerEl.createEl("h3", { text: frame.displayName || "Unnamed Frame" });
new Setting(this.containerEl)
.setName("Display Name")
.setDesc("The display name that this frame should have.")
.addText(t => {
t.setValue(frame.displayName);
t.onChange(async v => {
frame.displayName = v;
heading.setText(frame.displayName || "Unnamed Frame");
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl)
.setName("Icon")
.setDesc(createFragment(f => {
f.createSpan({ text: "The icon that this frame's pane should have." });
f.createEl("br");
f.createSpan({ text: "The names of any " });
f.createEl("a", { text: "Lucide icons", href: "https://lucide.dev/" });
f.createSpan({ text: " can be used." });
}))
.addText(t => {
t.setValue(frame.icon);
t.onChange(async v => {
frame.icon = v;
await this.plugin.saveSettings();
});
});
2022-03-22 13:17:10 +01:00
new Setting(this.containerEl)
.setName("URL")
.setDesc("The URL that should be opened in this frame.")
.addText(t => {
t.setValue(frame.url);
t.onChange(async v => {
frame.url = v;
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl)
.setName("Minimum Width")
2022-03-26 11:23:11 +01:00
.setDesc("The width that this frame's pane should be adjusted to automatically if it is lower. Set to 0 to disable.")
2022-03-22 13:17:10 +01:00
.addText(t => {
t.inputEl.type = "number";
t.setValue(String(frame.minimumWidth));
t.onChange(async v => {
frame.minimumWidth = v.length ? Number(v) : 0;
await this.plugin.saveSettings();
});
});
new Setting(this.containerEl)
.setName("Additional CSS")
2022-03-26 11:23:11 +01:00
.setDesc("A snippet of additional CSS that should be applied to this frame.")
2022-03-22 13:17:10 +01:00
.addTextArea(t => {
t.inputEl.rows = 5;
2022-03-22 13:17:10 +01:00
t.inputEl.cols = 50;
t.setValue(frame.customCss);
t.onChange(async v => {
frame.customCss = v;
await this.plugin.saveSettings();
});
});
new ButtonComponent(this.containerEl)
.setButtonText("Remove Frame")
.onClick(async () => {
this.plugin.settings.frames.remove(frame);
2022-03-20 13:57:02 +01:00
await this.plugin.saveSettings();
2022-03-22 13:17:10 +01:00
this.display();
2022-03-20 13:57:02 +01:00
});
2022-03-22 13:17:10 +01:00
}
this.containerEl.createEl("hr");
2022-03-23 16:13:11 +01:00
let info = this.containerEl.createEl("p", { text: "Create a new frame, either from a preset shipped with the plugin, or a custom one that you can edit yourself. Each frame's pane can be opened using the \"Custom Frames: Open\" command." });
info.createEl("br");
info.createSpan({ text: "Note that Obsidian has to be restarted or reloaded to activate a newly added frame.", cls: "mod-warning" });
2022-03-22 13:17:10 +01:00
let addDiv = this.containerEl.createDiv();
addDiv.addClass("custom-frames-add");
let dropdown = new DropdownComponent(addDiv);
dropdown.addOption("new", "Custom");
for (let key of Object.keys(presets))
dropdown.addOption(key, presets[key].displayName);
2022-03-22 13:17:10 +01:00
new ButtonComponent(addDiv)
.setButtonText("Add Frame")
.onClick(async () => {
let option = dropdown.getValue();
if (option == "new") {
this.plugin.settings.frames.push({
url: "",
displayName: "",
icon: "",
2022-03-22 13:17:10 +01:00
minimumWidth: 0,
customCss: ""
});
}
else {
this.plugin.settings.frames.push(presets[option]);
}
await this.plugin.saveSettings();
this.display();
2022-03-20 13:55:19 +01:00
});
2022-03-23 18:06:12 +01:00
this.containerEl.createEl("hr");
this.containerEl.createEl("p", { text: "If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!" });
this.containerEl.createEl("a", { href: "https://ellpeck.de/support" })
.createEl("img", { attr: { src: "https://ellpeck.de/res/generalsupport.png" }, cls: "custom-frames-support" });
2022-03-20 13:55:19 +01:00
}
2022-03-20 00:20:09 +01:00
}