mirror of
https://github.com/Ellpeck/ObsidianCustomFrames.git
synced 2024-11-22 09:43:30 +01:00
finished system!
This commit is contained in:
parent
672288135e
commit
cdbd29448a
5 changed files with 158 additions and 64 deletions
|
@ -1,5 +1,5 @@
|
||||||
# Obsidian Custom Frames
|
# Obsidian Custom Frames
|
||||||
An Obsidian plugin that allows adding iframes with custom styling as editor tabs
|
An Obsidian plugin that allows adding iframes with custom styling as editor tabs. Also comes with presets for Google Keep, Bring and more.
|
||||||
|
|
||||||
![A screenshot of the Obsidian Custom Frames plugin in action](screenshot.png)
|
![A screenshot of the Obsidian Custom Frames plugin in action](screenshot.png)
|
||||||
|
|
||||||
|
|
202
main.ts
202
main.ts
|
@ -1,20 +1,32 @@
|
||||||
import { App, ItemView, Plugin, PluginSettingTab, Setting, WorkspaceLeaf } from 'obsidian';
|
import { App, ButtonComponent, DropdownComponent, ItemView, Plugin, PluginSettingTab, Setting, WorkspaceLeaf } from "obsidian";
|
||||||
import { remote, webContents } from 'electron';
|
import { remote } from "electron";
|
||||||
|
|
||||||
const viewName: string = "keep";
|
|
||||||
const defaultSettings: CustomFramesSettings = {
|
const defaultSettings: CustomFramesSettings = {
|
||||||
minimumWidth: 370,
|
frames: [],
|
||||||
padding: 5,
|
padding: 5
|
||||||
css: `/* hide the menu bar and the "Keep" logo and text */
|
};
|
||||||
|
const presets: Record<string, CustomFrame> = {
|
||||||
|
"keep": {
|
||||||
|
url: "https://keep.google.com",
|
||||||
|
displayName: "Google Keep",
|
||||||
|
minimumWidth: 370,
|
||||||
|
customCss: `/* hide the menu bar and the "Keep" text */
|
||||||
.PvRhvb-qAWA2, .gb_2d.gb_Zc {
|
.PvRhvb-qAWA2, .gb_2d.gb_Zc {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}`
|
}`
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
interface CustomFramesSettings {
|
interface CustomFramesSettings {
|
||||||
minimumWidth: number;
|
frames: CustomFrame[];
|
||||||
padding: number;
|
padding: number;
|
||||||
css: string;
|
}
|
||||||
|
|
||||||
|
interface CustomFrame {
|
||||||
|
url: string;
|
||||||
|
displayName: string;
|
||||||
|
minimumWidth: number;
|
||||||
|
customCss: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CustomFramesPlugin extends Plugin {
|
export default class CustomFramesPlugin extends Plugin {
|
||||||
|
@ -24,24 +36,31 @@ export default class CustomFramesPlugin extends Plugin {
|
||||||
async onload(): Promise<void> {
|
async onload(): Promise<void> {
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
|
||||||
this.registerView(viewName, l => new CustomFrameView(l, this.settings));
|
for (let frame of this.settings.frames) {
|
||||||
this.addCommand({
|
if (!frame.url || !frame.displayName)
|
||||||
id: "open-keep",
|
continue;
|
||||||
name: "Open Keep",
|
let name = `custom-frames-${frame.displayName.toLowerCase().replace(/\s/g, "-")}`;
|
||||||
checkCallback: (checking: boolean) => {
|
try {
|
||||||
if (checking)
|
console.log(`Registering frame ${name} for URL ${frame.url}`);
|
||||||
return !this.app.workspace.getLeavesOfType(viewName).length;
|
|
||||||
this.openKeep();
|
this.registerView(name, l => new CustomFrameView(l, this.settings, frame, name));
|
||||||
},
|
this.addCommand({
|
||||||
});
|
id: `open-${name}`,
|
||||||
|
name: `Open ${frame.displayName}`,
|
||||||
|
checkCallback: (checking: boolean) => {
|
||||||
|
if (this.app.workspace.getLeavesOfType(name).length)
|
||||||
|
return false;
|
||||||
|
if (!checking)
|
||||||
|
this.app.workspace.getRightLeaf(false).setViewState({ type: name });
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
console.error(`Couldn't register frame ${name}, is there already one with the same name?`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.addSettingTab(new CustomFramesSettingTab(this.app, this));
|
this.addSettingTab(new CustomFramesSettingTab(this.app, this));
|
||||||
|
|
||||||
this.app.workspace.onLayoutReady(() => this.openKeep());
|
|
||||||
}
|
|
||||||
|
|
||||||
private openKeep(): void {
|
|
||||||
if (!this.app.workspace.getLeavesOfType(viewName).length)
|
|
||||||
this.app.workspace.getRightLeaf(false).setViewState({ type: viewName });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
|
@ -56,48 +75,52 @@ export default class CustomFramesPlugin extends Plugin {
|
||||||
class CustomFrameView extends ItemView {
|
class CustomFrameView extends ItemView {
|
||||||
|
|
||||||
private settings: CustomFramesSettings;
|
private settings: CustomFramesSettings;
|
||||||
|
private frame: CustomFrame;
|
||||||
|
private name: string;
|
||||||
|
|
||||||
constructor(leaf: WorkspaceLeaf, settings: CustomFramesSettings) {
|
constructor(leaf: WorkspaceLeaf, settings: CustomFramesSettings, frame: CustomFrame, name: string) {
|
||||||
super(leaf);
|
super(leaf);
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.frame = frame;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
onload(): void {
|
onload(): void {
|
||||||
this.contentEl.empty();
|
this.contentEl.empty();
|
||||||
this.contentEl.addClass("obsidian-keep-view");
|
this.contentEl.addClass("custom-frames-view");
|
||||||
|
|
||||||
let frame = this.contentEl.createEl("iframe");
|
let frame = this.contentEl.createEl("iframe");
|
||||||
|
frame.addClass("custom-frames-frame");
|
||||||
frame.setAttribute("style", `padding: ${this.settings.padding}px`);
|
frame.setAttribute("style", `padding: ${this.settings.padding}px`);
|
||||||
frame.addClass("obsidian-keep-frame");
|
|
||||||
frame.onload = () => {
|
frame.onload = () => {
|
||||||
for (let other of remote.getCurrentWebContents().mainFrame.frames) {
|
for (let other of remote.getCurrentWebContents().mainFrame.frames) {
|
||||||
if (frame.src.contains(new URL(other.url).host)) {
|
if (frame.src.contains(new URL(other.url).host)) {
|
||||||
other.executeJavaScript(`
|
other.executeJavaScript(`
|
||||||
let style = document.createElement("style");
|
let style = document.createElement("style");
|
||||||
style.textContent = \`${this.settings.css}\`;
|
style.textContent = \`${this.frame.customCss}\`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.settings.minimumWidth) {
|
if (this.frame.minimumWidth) {
|
||||||
let parent = this.contentEl.closest<HTMLElement>(".workspace-split.mod-horizontal");
|
let parent = this.contentEl.closest<HTMLElement>(".workspace-split.mod-horizontal");
|
||||||
if (parent) {
|
if (parent) {
|
||||||
let minWidth = `${this.settings.minimumWidth + 2 * this.settings.padding}px`;
|
let minWidth = `${this.frame.minimumWidth + 2 * this.settings.padding}px`;
|
||||||
if (parent.style.width < minWidth)
|
if (parent.style.width < minWidth)
|
||||||
parent.style.width = minWidth;
|
parent.style.width = minWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
frame.src = "https://keep.google.com";
|
frame.src = this.frame.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewType(): string {
|
getViewType(): string {
|
||||||
return viewName;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplayText(): string {
|
getDisplayText(): string {
|
||||||
return "Google Keep";
|
return this.frame.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIcon(): string {
|
getIcon(): string {
|
||||||
|
@ -116,22 +139,12 @@ class CustomFramesSettingTab extends PluginSettingTab {
|
||||||
|
|
||||||
display(): void {
|
display(): void {
|
||||||
this.containerEl.empty();
|
this.containerEl.empty();
|
||||||
this.containerEl.createEl('h2', { text: 'Obsidian Custom Frames Settings' });
|
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." });
|
||||||
|
|
||||||
new Setting(this.containerEl)
|
new Setting(this.containerEl)
|
||||||
.setName("Minimum View Width")
|
.setName("Frame Padding")
|
||||||
.setDesc("The width that the Google Keep view should be adjusted to automatically if it is lower. Set to 0 to disable.")
|
.setDesc("The padding that should be left around the inside of custom frame tabs, in pixels.")
|
||||||
.addText(t => {
|
|
||||||
t.inputEl.type = "number";
|
|
||||||
t.setValue(String(this.plugin.settings.minimumWidth));
|
|
||||||
t.onChange(async v => {
|
|
||||||
this.plugin.settings.minimumWidth = v.length ? Number(v) : defaultSettings.minimumWidth;
|
|
||||||
await this.plugin.saveSettings();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
new Setting(this.containerEl)
|
|
||||||
.setName("View Padding")
|
|
||||||
.setDesc("The padding that should be left around the inside of the Google Keep view, in pixels.")
|
|
||||||
.addText(t => {
|
.addText(t => {
|
||||||
t.inputEl.type = "number";
|
t.inputEl.type = "number";
|
||||||
t.setValue(String(this.plugin.settings.padding));
|
t.setValue(String(this.plugin.settings.padding));
|
||||||
|
@ -140,17 +153,90 @@ class CustomFramesSettingTab extends PluginSettingTab {
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
new Setting(this.containerEl)
|
|
||||||
.setName("Additional CSS")
|
for (let frame of this.plugin.settings.frames) {
|
||||||
.setDesc("A snippet of additional CSS that should be applied to the Google Keep embed. By default, this hides a lot of unnecessary information to make the embed take up less horizontal space.")
|
let heading = this.containerEl.createEl("h3", { text: frame.displayName || "Unnamed Frame" });
|
||||||
.addTextArea(t => {
|
|
||||||
t.inputEl.rows = 10;
|
new Setting(this.containerEl)
|
||||||
t.inputEl.cols = 50;
|
.setName("Display Name")
|
||||||
t.setValue(this.plugin.settings.css);
|
.setDesc("The display name that this frame should have.")
|
||||||
t.onChange(async v => {
|
.addText(t => {
|
||||||
this.plugin.settings.css = v.length ? v : defaultSettings.css;
|
t.setValue(frame.displayName);
|
||||||
await this.plugin.saveSettings();
|
t.onChange(async v => {
|
||||||
|
frame.displayName = v;
|
||||||
|
heading.setText(frame.displayName || "Unnamed Frame");
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
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")
|
||||||
|
.setDesc("The width that this frame's tab should be adjusted to automatically if it is lower. Set to 0 to disable.")
|
||||||
|
.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")
|
||||||
|
.setDesc("A snippet of additional CSS that should be applied to this frame.")
|
||||||
|
.addTextArea(t => {
|
||||||
|
t.inputEl.rows = 10;
|
||||||
|
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);
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
this.display();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.containerEl.createEl("hr");
|
||||||
|
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 tab can be opened using the 'Custom Frames: Open' command." });
|
||||||
|
|
||||||
|
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} Preset`);
|
||||||
|
new ButtonComponent(addDiv)
|
||||||
|
.setButtonText("Add Frame")
|
||||||
|
.onClick(async () => {
|
||||||
|
let option = dropdown.getValue();
|
||||||
|
if (option == "new") {
|
||||||
|
this.plugin.settings.frames.push({
|
||||||
|
url: "",
|
||||||
|
displayName: "",
|
||||||
|
minimumWidth: 0,
|
||||||
|
customCss: ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.plugin.settings.frames.push(presets[option]);
|
||||||
|
}
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
this.display();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"id": "obsidian-custom-frames",
|
"id": "obsidian-custom-frames",
|
||||||
"name": "Obsidian Custom Frames",
|
"name": "Custom Frames",
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"minAppVersion": "0.13.33",
|
"minAppVersion": "0.13.33",
|
||||||
"description": "An Obsidian plugin that allows adding iframes with custom styling as editor tabs",
|
"description": "A plugin that allows adding iframes with custom styling as editor tabs. Also comes with presets for Google Keep, Bring and more.",
|
||||||
"author": "Ellpeck",
|
"author": "Ellpeck",
|
||||||
"authorUrl": "https://ellpeck.de",
|
"authorUrl": "https://ellpeck.de",
|
||||||
"isDesktopOnly": true
|
"isDesktopOnly": true
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-custom-frames",
|
"name": "obsidian-custom-frames",
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"description": "An Obsidian plugin that allows adding iframes with custom styling as editor tabs",
|
"description": "An Obsidian plugin that allows adding iframes with custom styling as editor tabs. Also comes with presets for Google Keep, Bring and more.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
|
|
12
styles.css
12
styles.css
|
@ -1,10 +1,18 @@
|
||||||
.obsidian-keep-view {
|
.custom-frames-view {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.obsidian-keep-frame {
|
.custom-frames-frame {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-frames-add {
|
||||||
|
padding: 0 0 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-frames-add button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue