mirror of
https://github.com/Ellpeck/ObsidianCustomFrames.git
synced 2024-06-08 00:53:37 +02:00
Compare commits
34 commits
Author | SHA1 | Date | |
---|---|---|---|
Ell | 0cf9067abf | ||
Ell | 3526aaf70c | ||
Ell | b0a325a04f | ||
df1ddd5c17 | |||
0ec307eab6 | |||
Ell | bd10df45ee | ||
Ell | 0fc0e9e18d | ||
0cd6422f74 | |||
Ell | ab10797f39 | ||
Ell | 51b03d7bfc | ||
Ell | c1f085d26e | ||
Ell | d96c8baf8c | ||
Ell | 3ffe20f441 | ||
Ell | 21a8230e70 | ||
Ell | 256f00e727 | ||
880d4c975c | |||
Ell | 3ec5caf330 | ||
Ell | 2363a5e338 | ||
Ell | 72c14f63d7 | ||
Ell | 4340d08e29 | ||
Ell | 33f3407afe | ||
Ell | 2e5cbe42f5 | ||
Ell | 92db9fe55d | ||
Ell | 01ad91c8d4 | ||
Ell | 8db94a85f9 | ||
Ell | f2c73ef189 | ||
Ell | 0a70f83d46 | ||
Ell | a91735dc44 | ||
b425e9671f | |||
Ell | 14b01b67e5 | ||
Ell | 8b7c80d218 | ||
Ell | b805078fb1 | ||
Ell | e826e93d27 | ||
Ell | 5022385ee1 |
|
@ -1,9 +1,9 @@
|
||||||
# top-most EditorConfig file
|
# top-most EditorConfig file
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
indent_style = tab
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
tab_width = 4
|
tab_width = 4
|
||||||
|
|
45
.gitignore
vendored
45
.gitignore
vendored
|
@ -1,22 +1,23 @@
|
||||||
# vscode
|
# vscode
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# Intellij
|
# Intellij
|
||||||
*.iml
|
*.iml
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
# npm
|
# npm
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# Don't include the compiled main.js file in the repo.
|
# Don't include the compiled main.js file in the repo.
|
||||||
# They should be uploaded to GitHub releases instead.
|
# They should be uploaded to GitHub releases instead.
|
||||||
main.js
|
/main.js
|
||||||
|
|
||||||
# Exclude sourcemaps
|
# Exclude sourcemaps
|
||||||
*.map
|
*.map
|
||||||
|
|
||||||
# obsidian
|
# obsidian
|
||||||
data.json
|
workspace
|
||||||
|
workspace.json
|
||||||
# Exclude macOS Finder (System Explorer) View States
|
|
||||||
.DS_Store
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
|
.DS_Store
|
||||||
|
|
145
README.md
145
README.md
|
@ -1,70 +1,75 @@
|
||||||
# Obsidian Custom Frames
|
# Obsidian Custom Frames
|
||||||
An Obsidian plugin that turns web apps into panes using iframes with custom styling. Also comes with presets for Google Keep, Todoist and more.
|
An Obsidian plugin that turns web apps into panes using iframes with custom styling. Also comes with presets for Google Keep, Todoist and more.
|
||||||
|
|
||||||
![A screenshot of the plugin in action, where you can see Google Keep attached as a narrow side pane on the right](https://raw.githubusercontent.com/Ellpeck/ObsidianCustomFrames/master/screenshot.png)
|
![A screenshot of the plugin in action, where you can see Google Keep attached as a narrow side pane on the right](https://raw.githubusercontent.com/Ellpeck/ObsidianCustomFrames/master/screenshot.png)
|
||||||
|
|
||||||
![A screenshot of the plugin in action, where you can see Google Calendar opened in the center, and the mouse hovering over the corresponding ribbon button](https://raw.githubusercontent.com/Ellpeck/ObsidianCustomFrames/master/screenshot-big.png)
|
![A screenshot of the plugin in action, where you can see Google Calendar opened in the center, and the mouse hovering over the corresponding ribbon button](https://raw.githubusercontent.com/Ellpeck/ObsidianCustomFrames/master/screenshot-big.png)
|
||||||
|
|
||||||
![A screenshot of the plugin's settings](https://raw.githubusercontent.com/Ellpeck/ObsidianCustomFrames/master/settings.png)
|
![A screenshot of the plugin's settings](https://raw.githubusercontent.com/Ellpeck/ObsidianCustomFrames/master/settings.png)
|
||||||
|
|
||||||
## 🤔 Usage
|
## 🤔 Usage
|
||||||
To use this plugin, simply go into its settings and add a new frame, either from a preset shipped with the plugin, or a custom one that you can edit yourself.
|
To use this plugin, simply go into its settings and add a new frame, either from a preset shipped with the plugin, or a custom one that you can edit yourself.
|
||||||
|
|
||||||
### 🪟 Pane Mode
|
### 🪟 Pane Mode
|
||||||
To open a Custom Frame as a pane, you can use the "Custom Frames: Open" command.
|
To open a Custom Frame as a pane, you can use the "Custom Frames: Open" command.
|
||||||
|
|
||||||
There are also plenty of settings to customize your frame further, including adding custom CSS to the site, adding a ribbon icon, displaying the frame in the center of the editor, and more.
|
There are also plenty of settings to customize your frame further, including adding custom CSS to the site, adding a ribbon icon, displaying the frame in the center of the editor, and more.
|
||||||
|
|
||||||
### 🗒️ Markdown Mode
|
### 🗒️ Markdown Mode
|
||||||
You can also display your custom frames in your Markdown documents. Custom Frames adds a special code block syntax that transforms the code block into a custom frame in Live Preview and Reading mode. Your code block should look like this:
|
You can also display your custom frames in your Markdown documents. Custom Frames adds a special code block syntax that transforms the code block into a custom frame in Live Preview and Reading mode. Your code block should look like this:
|
||||||
~~~
|
~~~
|
||||||
```custom-frames
|
```custom-frames
|
||||||
frame: YOUR FRAME'S NAME
|
frame: YOUR FRAME'S NAME
|
||||||
```
|
```
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Optionally, you can also pass custom style settings to the embed, which allows you to change things like the embed's height, as well as an additional suffix that will be appended to the frame's regular URL, which can be useful for things like displaying a specific note in Google Keep.
|
Optionally, you can also pass custom style settings to the embed, which allows you to change things like the embed's height, as well as an additional suffix that will be appended to the frame's regular URL, which can be useful for things like displaying a specific note in Google Keep.
|
||||||
|
|
||||||
Here's an example using the [Google Keep preset](#-presets):
|
Here's an example using the [Google Keep preset](#-presets):
|
||||||
~~~
|
~~~
|
||||||
```custom-frames
|
```custom-frames
|
||||||
frame: Google Keep
|
frame: Google Keep
|
||||||
style: height: 1000px;
|
style: height: 1000px;
|
||||||
urlSuffix: #reminders
|
urlSuffix: #reminders
|
||||||
```
|
```
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
### 📱 On Obsidian Mobile
|
### 📱 On Obsidian Mobile
|
||||||
Unfortunately, Obsidian Mobile does not run on [Electron](https://www.electronjs.org/), which is what allows iframes and [webviews](https://www.electronjs.org/docs/latest/api/webview-tag) to be displayed with very few restrictions related to cookies, cross-origin resource sharing, and so on. This means that a lot of sites won't work there, especially ones that you have to log in to. However, when you create a frame, you can toggle the "Disable on Mobile" option to hide a Desktop-only frame in Obsidian mobile.
|
Unfortunately, Obsidian Mobile does not run on [Electron](https://www.electronjs.org/), which is what allows iframes and [webviews](https://www.electronjs.org/docs/latest/api/webview-tag) to be displayed with very few restrictions related to cookies, cross-origin resource sharing, and so on. This means that a lot of sites won't work there, especially ones that you have to log in to. However, when you create a frame, you can toggle the "Disable on Mobile" option to hide a Desktop-only frame in Obsidian mobile.
|
||||||
|
|
||||||
## 📦 Presets
|
Need help using the plugin? Feel free to join the Discord server!
|
||||||
By default, Custom Frames comes with a few presets that allow you to get new panes for popular sites up and running quickly.
|
|
||||||
- [Obsidian Forum](https://forum.obsidian.md/)
|
[![Join the Discord server](https://ellpeck.de/res/discord-wide.png)](https://link.ellpeck.de/discordweb)
|
||||||
- [Google Keep](https://keep.google.com), optimized for a narrow pane on the side
|
|
||||||
- [Google Calendar](https://calendar.google.com/calendar/u/0/r/day), optimized by removing some buttons. Close side panel with top-left button.
|
## 📦 Presets
|
||||||
- [Todoist](https://todoist.com), optimized for a narrow (half-height) side panel by removing some buttons and slimming margins.
|
By default, Custom Frames comes with a few presets that allow you to get new panes for popular sites up and running quickly.
|
||||||
- [Notion](https://www.notion.so/) (it's recommended to close Notion's sidebar if used as a side pane)
|
- [Obsidian Forum](https://forum.obsidian.md/)
|
||||||
- [Twitter](https://twitter.com)
|
- [Google Keep](https://keep.google.com), optimized for a narrow pane on the side
|
||||||
|
- [Google Calendar](https://calendar.google.com/calendar/u/0/r/day), optimized by removing some buttons. Close side panel with top-left button.
|
||||||
If you create a frame that you think other people would like, don't hesitate to create a pull request with [a new preset](https://github.com/Ellpeck/ObsidianCustomFrames/blob/master/src/settings.ts#L5).
|
- [Todoist](https://todoist.com), optimized for a narrow (half-height) side panel by removing some buttons and slimming margins.
|
||||||
|
- [Notion](https://www.notion.so/) (it's recommended to close Notion's sidebar if used as a side pane)
|
||||||
## 🛣️ Roadmap
|
- [Twitter](https://twitter.com)
|
||||||
- ~~Allow setting a custom icon for each pane~~
|
|
||||||
- ~~Allow displaying custom frames in Markdown code blocks~~
|
If you create a frame that you think other people would like, don't hesitate to create a pull request with [a new preset](https://github.com/Ellpeck/ObsidianCustomFrames/blob/master/src/settings.ts#L5).
|
||||||
- ~~Add the ability to add a ribbon button for a frame that opens it in the main view~~
|
|
||||||
- Allow creating links that open in a custom frame rather than the browser
|
## 🛣️ Roadmap
|
||||||
- Possibly allow executing custom JavaScript in iframes (though security implications still need to be explored)
|
- ~~Allow setting a custom icon for each pane~~
|
||||||
- Add a global setting that causes popups to be opened in a new Obsidian window rather than the default browser
|
- ~~Allow displaying custom frames in Markdown code blocks~~
|
||||||
- Add more options to Markdown mode, like allowing for back and forward buttons
|
- ~~Add the ability to add a ribbon button for a frame that opens it in the main view~~
|
||||||
|
- Allow creating links outside of Obsidian that open in a custom frame
|
||||||
## ⚠️ Known Issues
|
- Possibly allow executing custom JavaScript in iframes (though security implications still need to be explored)
|
||||||
There are a few known issues with Custom Frames. If you encounter any of these, please **don't** report it on the issue tracker.
|
- Add a global setting that causes popups to be opened in a new Obsidian window rather than the default browser
|
||||||
- In Obsidian **0.14.2 and lower**, a lot of websites don't function properly in custom frames. This is due to these older versions not having features in place that allow for frames to have special, additional functionality related to cookies and headers.
|
- Add more options to Markdown mode, like allowing for back and forward buttons
|
||||||
- In Obsidian **0.14.5 and lower**, when dragging or moving a pane, hovering the mouse over a custom frame will cause it to get stuck or behave unexpectedly.
|
- Possibly allow extracting selected text into a note similar to how the Note composer plugin works, and potentially allow using a note template that includes the link to the site extracted from
|
||||||
|
|
||||||
## 🙏 Acknowledgements
|
## ⚠️ Known Issues
|
||||||
Thanks to [lishid](https://github.com/lishid) for their help with making iframes work in Obsidian for a purpose like this. Also thanks to them for *motivating* me to turn Obsidian Keep into a more versatile plugin, which is how Custom Frames was born.
|
There are a few known issues with Custom Frames. If you encounter any of these, please **don't** report it on the issue tracker.
|
||||||
|
- Popups and new tabs are currently opened in the default browser rather than the custom frame. You can find more info, including workarounds for logging in to certain sites, in [this issue](https://github.com/Ellpeck/ObsidianCustomFrames/issues/40).
|
||||||
If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!
|
- Some links refuse to open from within custom frames, especially before Obsidian 1.3.7. You can find more info in [this issue](https://github.com/Ellpeck/ObsidianCustomFrames/issues/76).
|
||||||
|
|
||||||
[![Support me (if you want), via Patreon, Ko-fi or GitHub Sponsors](https://ellpeck.de/res/generalsupport.png)](https://ellpeck.de/support)
|
## 🙏 Acknowledgements
|
||||||
|
Thanks to [lishid](https://github.com/lishid) for their help with making iframes work in Obsidian for a purpose like this. Also thanks to them for *motivating* me to turn Obsidian Keep into a more versatile plugin, which is how Custom Frames was born.
|
||||||
|
|
||||||
|
If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!
|
||||||
|
|
||||||
|
[![Support me (if you want), via Patreon, Ko-fi or GitHub Sponsors](https://ellpeck.de/res/generalsupport-wide.png)](https://ellpeck.de/support)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
import process from "process";
|
import process from "process";
|
||||||
import builtins from 'builtin-modules';
|
import builtins from 'builtin-modules';
|
||||||
|
import {copy} from 'esbuild-plugin-copy';
|
||||||
|
|
||||||
const banner = `/*
|
const banner = `/*
|
||||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||||
|
@ -10,42 +11,50 @@ if you want to view the source, please visit the github repository of this plugi
|
||||||
const prod = (process.argv[2] === 'production');
|
const prod = (process.argv[2] === 'production');
|
||||||
|
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
banner: {
|
banner: {
|
||||||
js: banner,
|
js: banner,
|
||||||
},
|
},
|
||||||
entryPoints: ['src/main.ts'],
|
entryPoints: ['src/main.ts'],
|
||||||
bundle: true,
|
bundle: true,
|
||||||
external: [
|
external: [
|
||||||
'obsidian',
|
'obsidian',
|
||||||
'electron',
|
'electron',
|
||||||
'@codemirror/autocomplete',
|
'@codemirror/autocomplete',
|
||||||
'@codemirror/closebrackets',
|
'@codemirror/closebrackets',
|
||||||
'@codemirror/collab',
|
'@codemirror/collab',
|
||||||
'@codemirror/commands',
|
'@codemirror/commands',
|
||||||
'@codemirror/comment',
|
'@codemirror/comment',
|
||||||
'@codemirror/fold',
|
'@codemirror/fold',
|
||||||
'@codemirror/gutter',
|
'@codemirror/gutter',
|
||||||
'@codemirror/highlight',
|
'@codemirror/highlight',
|
||||||
'@codemirror/history',
|
'@codemirror/history',
|
||||||
'@codemirror/language',
|
'@codemirror/language',
|
||||||
'@codemirror/lint',
|
'@codemirror/lint',
|
||||||
'@codemirror/matchbrackets',
|
'@codemirror/matchbrackets',
|
||||||
'@codemirror/panel',
|
'@codemirror/panel',
|
||||||
'@codemirror/rangeset',
|
'@codemirror/rangeset',
|
||||||
'@codemirror/rectangular-selection',
|
'@codemirror/rectangular-selection',
|
||||||
'@codemirror/search',
|
'@codemirror/search',
|
||||||
'@codemirror/state',
|
'@codemirror/state',
|
||||||
'@codemirror/stream-parser',
|
'@codemirror/stream-parser',
|
||||||
'@codemirror/text',
|
'@codemirror/text',
|
||||||
'@codemirror/tooltip',
|
'@codemirror/tooltip',
|
||||||
'@codemirror/view',
|
'@codemirror/view',
|
||||||
...builtins
|
...builtins
|
||||||
],
|
],
|
||||||
format: 'cjs',
|
plugins: [
|
||||||
watch: !prod,
|
copy({
|
||||||
target: 'es2016',
|
assets: [{
|
||||||
logLevel: "info",
|
from: ["./manifest.json", "./main.js", "./styles.css"],
|
||||||
sourcemap: prod ? false : 'inline',
|
to: ["./test-vault/.obsidian/plugins/obsidian-custom-frames/."]
|
||||||
treeShaking: true,
|
}]
|
||||||
outfile: 'main.js',
|
}),
|
||||||
|
],
|
||||||
|
format: 'cjs',
|
||||||
|
watch: !prod,
|
||||||
|
target: 'es2016',
|
||||||
|
logLevel: "info",
|
||||||
|
sourcemap: prod ? false : 'inline',
|
||||||
|
treeShaking: true,
|
||||||
|
outfile: 'main.js',
|
||||||
}).catch(() => process.exit(1));
|
}).catch(() => process.exit(1));
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"id": "obsidian-custom-frames",
|
"id": "obsidian-custom-frames",
|
||||||
"name": "Custom Frames",
|
"name": "Custom Frames",
|
||||||
"version": "2.4.2",
|
"version": "2.4.7",
|
||||||
"minAppVersion": "0.14.5",
|
"minAppVersion": "1.2.0",
|
||||||
"description": "A plugin that turns web apps into panes using iframes with custom styling. Also comes with presets for Google Keep, Todoist and more.",
|
"description": "A plugin that turns web apps into panes using iframes with custom styling. Also comes with presets for Google Keep, Todoist and more.",
|
||||||
"author": "Ellpeck",
|
"author": "Ellpeck",
|
||||||
"authorUrl": "https://ellpeck.de",
|
"authorUrl": "https://ellpeck.de",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false
|
||||||
}
|
}
|
||||||
|
|
2634
package-lock.json
generated
2634
package-lock.json
generated
File diff suppressed because it is too large
Load diff
43
package.json
43
package.json
|
@ -1,23 +1,24 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-custom-frames",
|
"name": "obsidian-custom-frames",
|
||||||
"version": "2.4.2",
|
"version": "2.4.7",
|
||||||
"description": "An Obsidian plugin that turns web apps into panes using iframes with custom styling. Also comes with presets for Google Keep, Todoist and more.",
|
"description": "An Obsidian plugin that turns web apps into panes using iframes with custom styling. Also comes with presets for Google Keep, Todoist and more.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Ellpeck",
|
"author": "Ellpeck",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.11.6",
|
"@types/node": "^16.11.6",
|
||||||
"builtin-modules": "^3.2.0",
|
"builtin-modules": "^3.2.0",
|
||||||
"electron": "^13.6.2",
|
"electron": "^20.3.9",
|
||||||
"esbuild": "0.13.12",
|
"esbuild": "0.14.0",
|
||||||
"obsidian": "latest",
|
"esbuild-plugin-copy": "^1.3.0",
|
||||||
"tslib": "2.3.1",
|
"obsidian": "latest",
|
||||||
"typescript": "4.4.4"
|
"tslib": "2.3.1",
|
||||||
}
|
"typescript": "4.4.4"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
58
src/frame.ts
58
src/frame.ts
|
@ -1,5 +1,5 @@
|
||||||
import { Platform } from "obsidian";
|
import { Platform } from "obsidian";
|
||||||
import { CustomFrameSettings, CustomFramesSettings } from "./settings";
|
import { CustomFrameSettings, CustomFramesSettings, getId } from "./settings";
|
||||||
|
|
||||||
export class CustomFrame {
|
export class CustomFrame {
|
||||||
|
|
||||||
|
@ -12,25 +12,36 @@ export class CustomFrame {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public create(additionalStyle: string = undefined, urlSuffix: string = undefined): any {
|
create(parent: HTMLElement, additionalStyle: string = undefined, urlSuffix: string = undefined): void {
|
||||||
let style = `padding: ${this.settings.padding}px;`;
|
let style = `padding: ${this.settings.padding}px;`;
|
||||||
if (additionalStyle)
|
if (additionalStyle)
|
||||||
style += additionalStyle;
|
style += additionalStyle;
|
||||||
if (Platform.isDesktopApp && !this.data.forceIframe) {
|
if (Platform.isDesktopApp && !this.data.forceIframe) {
|
||||||
this.frame = document.createElement("webview");
|
let frameDoc = parent.doc;
|
||||||
|
this.frame = frameDoc.createElement("webview");
|
||||||
|
parent.appendChild(this.frame);
|
||||||
this.frame.setAttribute("allowpopups", "");
|
this.frame.setAttribute("allowpopups", "");
|
||||||
this.frame.addEventListener("dom-ready", () => {
|
this.frame.addEventListener("dom-ready", () => {
|
||||||
this.frame.setZoomFactor(this.data.zoomLevel);
|
this.frame.setZoomFactor(this.data.zoomLevel);
|
||||||
this.frame.insertCSS(this.data.customCss);
|
this.frame.insertCSS(this.data.customCss);
|
||||||
|
this.frame.executeJavaScript(this.data.customJs)
|
||||||
});
|
});
|
||||||
}
|
this.frame.addEventListener("destroyed", () => {
|
||||||
else {
|
// recreate the webview if it was moved to a new window
|
||||||
this.frame = document.createElement("iframe");
|
if (frameDoc != parent.doc) {
|
||||||
this.frame.setAttribute("sandbox", "allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts allow-top-navigation-by-user-activation");
|
this.frame.detach();
|
||||||
|
this.create(parent, additionalStyle, urlSuffix);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.frame = parent.doc.createElement("iframe");
|
||||||
|
parent.appendChild(this.frame);
|
||||||
|
this.frame.setAttribute("sandbox", "allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts allow-top-navigation-by-user-activation allow-downloads");
|
||||||
this.frame.setAttribute("allow", "encrypted-media; fullscreen; oversized-images; picture-in-picture; sync-xhr; geolocation;");
|
this.frame.setAttribute("allow", "encrypted-media; fullscreen; oversized-images; picture-in-picture; sync-xhr; geolocation;");
|
||||||
style += `transform: scale(${this.data.zoomLevel}); transform-origin: 0 0;`;
|
style += `transform: scale(${this.data.zoomLevel}); transform-origin: 0 0;`;
|
||||||
}
|
}
|
||||||
this.frame.addClass("custom-frames-frame");
|
this.frame.addClass("custom-frames-frame");
|
||||||
|
this.frame.addClass(`custom-frames-${getId(this.data)}`);
|
||||||
this.frame.setAttribute("style", style);
|
this.frame.setAttribute("style", style);
|
||||||
|
|
||||||
let src = this.data.url;
|
let src = this.data.url;
|
||||||
|
@ -40,11 +51,9 @@ export class CustomFrame {
|
||||||
src += urlSuffix;
|
src += urlSuffix;
|
||||||
}
|
}
|
||||||
this.frame.setAttribute("src", src);
|
this.frame.setAttribute("src", src);
|
||||||
|
|
||||||
return this.frame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public refresh(): void {
|
refresh(): void {
|
||||||
if (this.frame instanceof HTMLIFrameElement) {
|
if (this.frame instanceof HTMLIFrameElement) {
|
||||||
this.frame.contentWindow.location.reload();
|
this.frame.contentWindow.location.reload();
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,7 +61,7 @@ export class CustomFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public return(): void {
|
return(): void {
|
||||||
if (this.frame instanceof HTMLIFrameElement) {
|
if (this.frame instanceof HTMLIFrameElement) {
|
||||||
this.frame.contentWindow.open(this.data.url);
|
this.frame.contentWindow.open(this.data.url);
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,36 +69,41 @@ export class CustomFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public goBack(): void {
|
goBack(): void {
|
||||||
if (this.frame instanceof HTMLIFrameElement) {
|
if (this.frame instanceof HTMLIFrameElement) {
|
||||||
this.frame.contentWindow.history.back();
|
this.frame.contentWindow.history.back();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.frame.goBack();
|
this.frame.goBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public goForward(): void {
|
goForward(): void {
|
||||||
if (this.frame instanceof HTMLIFrameElement) {
|
if (this.frame instanceof HTMLIFrameElement) {
|
||||||
this.frame.contentWindow.history.forward();
|
this.frame.contentWindow.history.forward();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.frame.goForward();
|
this.frame.goForward();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleDevTools(): void {
|
toggleDevTools(): void {
|
||||||
if (!(this.frame instanceof HTMLIFrameElement)) {
|
if (!(this.frame instanceof HTMLIFrameElement)) {
|
||||||
if (!this.frame.isDevToolsOpened()) {
|
if (!this.frame.isDevToolsOpened()) {
|
||||||
this.frame.openDevTools();
|
this.frame.openDevTools();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.frame.closeDevTools();
|
this.frame.closeDevTools();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCurrentUrl(): string {
|
getCurrentUrl(): string {
|
||||||
return this.frame instanceof HTMLIFrameElement ? this.frame.contentWindow.location.href : this.frame.getURL();
|
return this.frame instanceof HTMLIFrameElement ? this.frame.contentWindow.location.href : this.frame.getURL();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
focus(): void {
|
||||||
|
if (this.frame instanceof HTMLIFrameElement) {
|
||||||
|
this.frame.contentWindow.focus();
|
||||||
|
} else {
|
||||||
|
this.frame.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
154
src/main.ts
154
src/main.ts
|
@ -1,95 +1,97 @@
|
||||||
import { Plugin, Platform } from "obsidian";
|
import { Plugin, Platform, WorkspaceLeaf } from "obsidian";
|
||||||
import { CustomFrame } from "./frame";
|
import { CustomFrame } from "./frame";
|
||||||
import { CustomFramesSettings, defaultSettings, getIcon } from "./settings";
|
import { CustomFramesSettings, defaultSettings, getIcon, getId } from "./settings";
|
||||||
import { CustomFramesSettingTab } from "./settings-tab";
|
import { CustomFramesSettingTab } from "./settings-tab";
|
||||||
import { CustomFrameView } from "./view";
|
import { CustomFrameView } from "./view";
|
||||||
|
|
||||||
export default class CustomFramesPlugin extends Plugin {
|
export default class CustomFramesPlugin extends Plugin {
|
||||||
|
|
||||||
settings: CustomFramesSettings;
|
settings: CustomFramesSettings;
|
||||||
|
|
||||||
async onload(): Promise<void> {
|
async onload(): Promise<void> {
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
|
||||||
for (let frame of this.settings.frames) {
|
for (let frame of this.settings.frames) {
|
||||||
if (!frame.url || !frame.displayName)
|
if (!frame.url || !frame.displayName)
|
||||||
continue;
|
continue;
|
||||||
let name = `custom-frames-${frame.displayName.toLowerCase().replace(/\s/g, "-")}`;
|
let name = `custom-frames-${getId(frame)}`;
|
||||||
if (Platform.isMobileApp && frame.hideOnMobile) {
|
if (Platform.isMobileApp && frame.hideOnMobile) {
|
||||||
console.log(`Skipping frame ${name} which is hidden on mobile`);
|
console.log(`Skipping frame ${name} which is hidden on mobile`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
console.log(`Registering frame ${name} for URL ${frame.url}`);
|
console.log(`Registering frame ${name} for URL ${frame.url}`);
|
||||||
|
|
||||||
this.registerView(name, l => new CustomFrameView(l, this.settings, frame, name));
|
this.registerView(name, l => new CustomFrameView(l, this.settings, frame, name));
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: `open-${name}`,
|
id: `open-${name}`,
|
||||||
name: `Open ${frame.displayName}`,
|
name: `Open ${frame.displayName}`,
|
||||||
callback: () => this.openLeaf(name, frame.openInCenter, false),
|
callback: () => this.openLeaf(name, frame.openInCenter, false),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (frame.addRibbonIcon)
|
if (frame.addRibbonIcon)
|
||||||
this.addRibbonIcon(getIcon(frame), `Open ${frame.displayName}`,
|
this.addRibbonIcon(getIcon(frame), `Open ${frame.displayName}`,
|
||||||
e => this.openLeaf(name, frame.openInCenter, Platform.isMacOS ? e.metaKey : e.ctrlKey));
|
e => this.openLeaf(name, frame.openInCenter, Platform.isMacOS ? e.metaKey : e.ctrlKey));
|
||||||
} catch {
|
} catch {
|
||||||
console.error(`Couldn't register frame ${name}, is there already one with the same name?`);
|
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.registerMarkdownCodeBlockProcessor("custom-frames", (s, e) => {
|
this.registerMarkdownCodeBlockProcessor("custom-frames", (s, e) => {
|
||||||
e.empty();
|
e.empty();
|
||||||
e.addClass("custom-frames-view-file");
|
e.addClass("custom-frames-view-file");
|
||||||
|
|
||||||
let frameMatch = /frame:([^\n]+)/gi.exec(s);
|
let frameMatch = /frame:([^\n]+)/gi.exec(s);
|
||||||
let frameName = frameMatch && frameMatch[1].trim();
|
let frameName = frameMatch && frameMatch[1].trim();
|
||||||
if (!frameName) {
|
if (!frameName) {
|
||||||
e.createSpan({ text: "Couldn't parse frame name" });
|
e.createSpan({ text: "Couldn't parse frame name" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data = this.settings.frames.find(f => f.displayName == frameName);
|
let data = this.settings.frames.find(f => f.displayName == frameName);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
e.createSpan({ text: `Couldn't find a frame with name ${frameName}` });
|
e.createSpan({ text: `Couldn't find a frame with name ${frameName}` });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Platform.isMobileApp && data.hideOnMobile) {
|
if (Platform.isMobileApp && data.hideOnMobile) {
|
||||||
e.createSpan({ text: `${frameName} is hidden on mobile` });
|
e.createSpan({ text: `${frameName} is hidden on mobile` });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let styleMatch = /style:([^\n]+)/gi.exec(s);
|
let styleMatch = /style:([^\n]+)/gi.exec(s);
|
||||||
let style = styleMatch && styleMatch[1].trim();
|
let style = styleMatch && styleMatch[1].trim();
|
||||||
style ||= "height: 600px;";
|
style ||= "height: 600px;";
|
||||||
|
|
||||||
let urlSuffixMatch = /urlsuffix:([^\n]+)/gi.exec(s);
|
let urlSuffixMatch = /urlsuffix:([^\n]+)/gi.exec(s);
|
||||||
let urlSuffix = urlSuffixMatch && urlSuffixMatch[1].trim();
|
let urlSuffix = urlSuffixMatch && urlSuffixMatch[1].trim();
|
||||||
urlSuffix ||= "";
|
urlSuffix ||= "";
|
||||||
|
|
||||||
let frame = new CustomFrame(this.settings, data);
|
let frame = new CustomFrame(this.settings, data);
|
||||||
e.appendChild(frame.create(style, urlSuffix));
|
frame.create(e, style, urlSuffix);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
this.settings = Object.assign({}, defaultSettings, await this.loadData());
|
this.settings = Object.assign({}, defaultSettings, await this.loadData());
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
await this.saveData(this.settings);
|
await this.saveData(this.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openLeaf(name: string, center: boolean, split: boolean): Promise<void> {
|
private async openLeaf(name: string, center: boolean, split: boolean): Promise<void> {
|
||||||
if (center) {
|
let leaf: WorkspaceLeaf;
|
||||||
this.app.workspace.detachLeavesOfType(name);
|
if (center) {
|
||||||
let leaf = split ? this.app.workspace.splitActiveLeaf() : this.app.workspace.getUnpinnedLeaf();
|
leaf = this.app.workspace.getLeaf(split);
|
||||||
await leaf.setViewState({ type: name });
|
await leaf.setViewState({ type: name, active: true });
|
||||||
}
|
} else {
|
||||||
else {
|
if (!this.app.workspace.getLeavesOfType(name).length)
|
||||||
if (!this.app.workspace.getLeavesOfType(name).length)
|
await this.app.workspace.getRightLeaf(false).setViewState({ type: name, active: true });
|
||||||
await this.app.workspace.getRightLeaf(false).setViewState({ type: name });
|
leaf = this.app.workspace.getLeavesOfType(name)[0];
|
||||||
}
|
this.app.workspace.revealLeaf(leaf);
|
||||||
this.app.workspace.revealLeaf(this.app.workspace.getLeavesOfType(name)[0]);
|
}
|
||||||
}
|
if (leaf.view instanceof CustomFrameView)
|
||||||
}
|
leaf.view.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,10 @@ export class CustomFramesSettingTab extends PluginSettingTab {
|
||||||
display(): void {
|
display(): void {
|
||||||
this.containerEl.empty();
|
this.containerEl.empty();
|
||||||
this.containerEl.createEl("h2", { text: "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.", cls: "mod-warning" });
|
this.containerEl.createEl("p", {
|
||||||
|
text: "Please note that Obsidian has to be restarted or reloaded for most of these settings to take effect.",
|
||||||
|
cls: "mod-warning"
|
||||||
|
});
|
||||||
|
|
||||||
new Setting(this.containerEl)
|
new Setting(this.containerEl)
|
||||||
.setName("Frame Padding")
|
.setName("Frame Padding")
|
||||||
|
@ -146,6 +149,22 @@ export class CustomFramesSettingTab extends PluginSettingTab {
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
new Setting(content)
|
||||||
|
.setName("Additional JavaScript")
|
||||||
|
.setDesc(createFragment(f => {
|
||||||
|
f.createSpan({ text: "A snippet of additional JavaScript that should be applied to this frame." });
|
||||||
|
f.createEl("br");
|
||||||
|
f.createEl("em", { text: "Note that this is only applied on Desktop." });
|
||||||
|
}))
|
||||||
|
.addTextArea(t => {
|
||||||
|
t.inputEl.rows = 5;
|
||||||
|
t.inputEl.cols = 50;
|
||||||
|
t.setValue(frame.customJs);
|
||||||
|
t.onChange(async v => {
|
||||||
|
frame.customJs = v;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
new ButtonComponent(content)
|
new ButtonComponent(content)
|
||||||
.setButtonText("Remove Frame")
|
.setButtonText("Remove Frame")
|
||||||
.onClick(async () => {
|
.onClick(async () => {
|
||||||
|
@ -156,9 +175,7 @@ export class CustomFramesSettingTab extends PluginSettingTab {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.containerEl.createEl("hr");
|
this.containerEl.createEl("hr");
|
||||||
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." });
|
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" });
|
|
||||||
|
|
||||||
let addDiv = this.containerEl.createDiv();
|
let addDiv = this.containerEl.createDiv();
|
||||||
let dropdown = new DropdownComponent(addDiv);
|
let dropdown = new DropdownComponent(addDiv);
|
||||||
|
@ -180,19 +197,30 @@ export class CustomFramesSettingTab extends PluginSettingTab {
|
||||||
openInCenter: false,
|
openInCenter: false,
|
||||||
zoomLevel: 1,
|
zoomLevel: 1,
|
||||||
forceIframe: false,
|
forceIframe: false,
|
||||||
customCss: ""
|
customCss: "",
|
||||||
|
customJs: ""
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.plugin.settings.frames.push(presets[option]);
|
this.plugin.settings.frames.push(presets[option]);
|
||||||
}
|
}
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
this.display();
|
this.display();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let disclaimer = this.containerEl.createEl("p", { cls: "mod-warning" });
|
||||||
|
disclaimer.createSpan({ text: "Please be advised that, when adding a site as a custom frame, you potentially expose personal information you enter to other plugins you have installed. For more information, see " });
|
||||||
|
disclaimer.createEl("a", {
|
||||||
|
text: "this discussion",
|
||||||
|
href: "https://github.com/Ellpeck/ObsidianCustomFrames/issues/54#issuecomment-1210879685",
|
||||||
|
cls: "mod-warning"
|
||||||
|
});
|
||||||
|
disclaimer.createSpan({ text: "." });
|
||||||
|
|
||||||
this.containerEl.createEl("hr");
|
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("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" })
|
this.containerEl.createEl("a", { href: "https://ellpeck.de/support" }).createEl("img", {
|
||||||
.createEl("img", { attr: { src: "https://ellpeck.de/res/generalsupport.png" }, cls: "custom-frames-support" });
|
attr: { src: "https://ellpeck.de/res/generalsupport.png" },
|
||||||
|
cls: "custom-frames-support"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,27 @@ export const presets: Record<string, CustomFrameSettings> = {
|
||||||
openInCenter: true,
|
openInCenter: true,
|
||||||
zoomLevel: 1,
|
zoomLevel: 1,
|
||||||
forceIframe: false,
|
forceIframe: false,
|
||||||
customCss: ""
|
customCss: "",
|
||||||
|
customJs: ""
|
||||||
|
},
|
||||||
|
"detexify": {
|
||||||
|
url: "https://detexify.kirelabs.org/classify.html",
|
||||||
|
displayName: "Detexify",
|
||||||
|
icon: "type",
|
||||||
|
hideOnMobile: true,
|
||||||
|
addRibbonIcon: true,
|
||||||
|
openInCenter: false,
|
||||||
|
zoomLevel: .95,
|
||||||
|
forceIframe: false,
|
||||||
|
customCss: `/* hide info clutter and ad banner */
|
||||||
|
#classify--info-area,
|
||||||
|
.adsbygoogle {
|
||||||
|
display: none !important
|
||||||
|
}`,
|
||||||
|
customJs: ""
|
||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
url: "https://calendar.google.com/calendar/u/0/r/day",
|
url: "https://calendar.google.com/calendar",
|
||||||
displayName: "Google Calendar",
|
displayName: "Google Calendar",
|
||||||
icon: "calendar",
|
icon: "calendar",
|
||||||
hideOnMobile: true,
|
hideOnMobile: true,
|
||||||
|
@ -23,16 +40,15 @@ export const presets: Record<string, CustomFrameSettings> = {
|
||||||
openInCenter: true,
|
openInCenter: true,
|
||||||
zoomLevel: 1,
|
zoomLevel: 1,
|
||||||
forceIframe: false,
|
forceIframe: false,
|
||||||
customCss: `/* hide right-side menu, and some buttons */
|
customCss: `/* hide the menu bar "Calendar" text and remove minimum width */
|
||||||
div.d6McF,
|
div[style*="min-width: 238px"] {
|
||||||
div.pw6cBb,
|
min-width: 0 !important;
|
||||||
div.gb_Td.gb_Va.gb_Id,
|
padding-right: 0 !important;
|
||||||
div.Kk7lMc-QWPxkf-LgbsSe-haAclf,
|
}
|
||||||
div.h8Aqhb,
|
div[style*="min-width: 238px"] span[role*="heading"] {
|
||||||
div.gboEAb,
|
|
||||||
div.dwlvNd {
|
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}`
|
}`,
|
||||||
|
customJs: ""
|
||||||
},
|
},
|
||||||
"keep": {
|
"keep": {
|
||||||
url: "https://keep.google.com",
|
url: "https://keep.google.com",
|
||||||
|
@ -43,11 +59,17 @@ div.dwlvNd {
|
||||||
openInCenter: false,
|
openInCenter: false,
|
||||||
zoomLevel: 1,
|
zoomLevel: 1,
|
||||||
forceIframe: false,
|
forceIframe: false,
|
||||||
customCss: `/* hide the menu bar and the "Keep" text */
|
customCss: `/* hide the menu bar, the "Keep" text and the Google Apps button */
|
||||||
html > body > div:nth-child(2) > div:nth-child(2) > div:first-child,
|
html > body > div:nth-child(2) > div:nth-child(2) > div:first-child,
|
||||||
html > body > div:first-child > header:first-child > div > div:first-child > div > div:first-child > a:first-child > span {
|
html > body > div:first-child > header:first-child > div > div:first-child > div > div:first-child > a:first-child > span,
|
||||||
display: none !important;
|
html > body > div:first-child > header:first-child > div:nth-child(2) > div:first-child > div:first-child,
|
||||||
}`
|
html > body > div:first-child > header:first-child > div:nth-child(2) > div:nth-child(3) > div:first-child > div:first-child > div:first-child {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
html > body > div:first-child > header:first-child > div > div:first-child > div > div:first-child > a:first-child {
|
||||||
|
cursor: default;
|
||||||
|
}`,
|
||||||
|
customJs: ""
|
||||||
},
|
},
|
||||||
"todoist": {
|
"todoist": {
|
||||||
url: "https://todoist.com",
|
url: "https://todoist.com",
|
||||||
|
@ -74,7 +96,8 @@ html > body > div:first-child > header:first-child > div > div:first-child > div
|
||||||
|
|
||||||
.undo_toast {
|
.undo_toast {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}`
|
}`,
|
||||||
|
customJs: ""
|
||||||
},
|
},
|
||||||
"notion": {
|
"notion": {
|
||||||
url: "https://www.notion.so/",
|
url: "https://www.notion.so/",
|
||||||
|
@ -85,7 +108,8 @@ html > body > div:first-child > header:first-child > div > div:first-child > div
|
||||||
openInCenter: true,
|
openInCenter: true,
|
||||||
zoomLevel: 1,
|
zoomLevel: 1,
|
||||||
forceIframe: false,
|
forceIframe: false,
|
||||||
customCss: ""
|
customCss: "",
|
||||||
|
customJs: ""
|
||||||
},
|
},
|
||||||
"twitter": {
|
"twitter": {
|
||||||
url: "https://twitter.com",
|
url: "https://twitter.com",
|
||||||
|
@ -96,7 +120,20 @@ html > body > div:first-child > header:first-child > div > div:first-child > div
|
||||||
openInCenter: false,
|
openInCenter: false,
|
||||||
zoomLevel: 1,
|
zoomLevel: 1,
|
||||||
forceIframe: false,
|
forceIframe: false,
|
||||||
customCss: ""
|
customCss: "",
|
||||||
|
customJs: ""
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
url: "https://tasks.google.com/embed/?origin=https://calendar.google.com&fullWidth=1",
|
||||||
|
displayName: "Google Tasks",
|
||||||
|
icon: "list-checks",
|
||||||
|
hideOnMobile: true,
|
||||||
|
addRibbonIcon: false,
|
||||||
|
openInCenter: false,
|
||||||
|
zoomLevel: 1,
|
||||||
|
forceIframe: false,
|
||||||
|
customCss: "",
|
||||||
|
customJs: ""
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,8 +152,13 @@ export interface CustomFrameSettings {
|
||||||
zoomLevel: number;
|
zoomLevel: number;
|
||||||
forceIframe: boolean;
|
forceIframe: boolean;
|
||||||
customCss: string;
|
customCss: string;
|
||||||
|
customJs: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIcon(settings: CustomFrameSettings) {
|
export function getIcon(settings: CustomFrameSettings) {
|
||||||
return settings.icon ? `lucide-${settings.icon}` : "documents";
|
return settings.icon ? `lucide-${settings.icon}` : "documents";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getId(settings: CustomFrameSettings) {
|
||||||
|
return settings.displayName.toLowerCase().replace(/\s/g, "-");
|
||||||
|
}
|
||||||
|
|
13
src/view.ts
13
src/view.ts
|
@ -45,6 +45,7 @@ export class CustomFrameView extends ItemView {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.frame = new CustomFrame(settings, data);
|
this.frame = new CustomFrame(settings, data);
|
||||||
|
this.navigation = data.openInCenter;
|
||||||
|
|
||||||
for (let action of CustomFrameView.actions)
|
for (let action of CustomFrameView.actions)
|
||||||
this.addAction(action.icon, action.name, () => action.action(this));
|
this.addAction(action.icon, action.name, () => action.action(this));
|
||||||
|
@ -53,11 +54,11 @@ export class CustomFrameView extends ItemView {
|
||||||
onload(): void {
|
onload(): void {
|
||||||
this.contentEl.empty();
|
this.contentEl.empty();
|
||||||
this.contentEl.addClass("custom-frames-view");
|
this.contentEl.addClass("custom-frames-view");
|
||||||
this.contentEl.appendChild(this.frame.create());
|
this.frame.create(this.contentEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
onHeaderMenu(menu: Menu): void {
|
onPaneMenu(menu: Menu, source: string): void {
|
||||||
super.onHeaderMenu(menu);
|
super.onPaneMenu(menu, source);
|
||||||
for (let action of CustomFrameView.actions) {
|
for (let action of CustomFrameView.actions) {
|
||||||
menu.addItem(i => {
|
menu.addItem(i => {
|
||||||
i.setTitle(action.name);
|
i.setTitle(action.name);
|
||||||
|
@ -78,10 +79,14 @@ export class CustomFrameView extends ItemView {
|
||||||
getIcon(): string {
|
getIcon(): string {
|
||||||
return getIcon(this.data);
|
return getIcon(this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focus(): void {
|
||||||
|
this.frame.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Action {
|
interface Action {
|
||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
action: (view: CustomFrameView) => any;
|
action: (view: CustomFrameView) => any;
|
||||||
}
|
}
|
||||||
|
|
28
styles.css
28
styles.css
|
@ -1,31 +1,31 @@
|
||||||
.custom-frames-view {
|
.custom-frames-view {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-frames-view-file {
|
.custom-frames-view-file {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-frames-frame {
|
.custom-frames-frame {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
background-clip: content-box;
|
background-clip: content-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-frames-add {
|
.custom-frames-add {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-frames-show {
|
.custom-frames-show {
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-frames-support {
|
.custom-frames-support {
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
3
test-vault/.obsidian/app.json
vendored
Normal file
3
test-vault/.obsidian/app.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"promptDelete": false
|
||||||
|
}
|
3
test-vault/.obsidian/appearance.json
vendored
Normal file
3
test-vault/.obsidian/appearance.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"accentColor": ""
|
||||||
|
}
|
3
test-vault/.obsidian/community-plugins.json
vendored
Normal file
3
test-vault/.obsidian/community-plugins.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[
|
||||||
|
"obsidian-custom-frames"
|
||||||
|
]
|
30
test-vault/.obsidian/core-plugins-migration.json
vendored
Normal file
30
test-vault/.obsidian/core-plugins-migration.json
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"file-explorer": true,
|
||||||
|
"global-search": true,
|
||||||
|
"switcher": true,
|
||||||
|
"graph": true,
|
||||||
|
"backlink": true,
|
||||||
|
"outgoing-link": true,
|
||||||
|
"tag-pane": true,
|
||||||
|
"page-preview": true,
|
||||||
|
"daily-notes": true,
|
||||||
|
"templates": true,
|
||||||
|
"note-composer": true,
|
||||||
|
"command-palette": true,
|
||||||
|
"slash-command": false,
|
||||||
|
"editor-status": true,
|
||||||
|
"starred": true,
|
||||||
|
"markdown-importer": false,
|
||||||
|
"zk-prefixer": false,
|
||||||
|
"random-note": false,
|
||||||
|
"outline": true,
|
||||||
|
"word-count": true,
|
||||||
|
"slides": false,
|
||||||
|
"audio-recorder": false,
|
||||||
|
"workspaces": false,
|
||||||
|
"file-recovery": true,
|
||||||
|
"publish": false,
|
||||||
|
"sync": false,
|
||||||
|
"canvas": true,
|
||||||
|
"bookmarks": true
|
||||||
|
}
|
20
test-vault/.obsidian/core-plugins.json
vendored
Normal file
20
test-vault/.obsidian/core-plugins.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[
|
||||||
|
"file-explorer",
|
||||||
|
"global-search",
|
||||||
|
"switcher",
|
||||||
|
"graph",
|
||||||
|
"backlink",
|
||||||
|
"canvas",
|
||||||
|
"outgoing-link",
|
||||||
|
"tag-pane",
|
||||||
|
"page-preview",
|
||||||
|
"daily-notes",
|
||||||
|
"templates",
|
||||||
|
"note-composer",
|
||||||
|
"command-palette",
|
||||||
|
"editor-status",
|
||||||
|
"bookmarks",
|
||||||
|
"outline",
|
||||||
|
"word-count",
|
||||||
|
"file-recovery"
|
||||||
|
]
|
22
test-vault/.obsidian/graph.json
vendored
Normal file
22
test-vault/.obsidian/graph.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"collapse-filter": true,
|
||||||
|
"search": "",
|
||||||
|
"showTags": false,
|
||||||
|
"showAttachments": false,
|
||||||
|
"hideUnresolved": false,
|
||||||
|
"showOrphans": true,
|
||||||
|
"collapse-color-groups": true,
|
||||||
|
"colorGroups": [],
|
||||||
|
"collapse-display": true,
|
||||||
|
"showArrow": false,
|
||||||
|
"textFadeMultiplier": 0,
|
||||||
|
"nodeSizeMultiplier": 1,
|
||||||
|
"lineSizeMultiplier": 1,
|
||||||
|
"collapse-forces": true,
|
||||||
|
"centerStrength": 0.518713248970312,
|
||||||
|
"repelStrength": 10,
|
||||||
|
"linkStrength": 1,
|
||||||
|
"linkDistance": 250,
|
||||||
|
"scale": 1,
|
||||||
|
"close": false
|
||||||
|
}
|
11
test-vault/.obsidian/hotkeys.json
vendored
Normal file
11
test-vault/.obsidian/hotkeys.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"app:reload": [
|
||||||
|
{
|
||||||
|
"modifiers": [
|
||||||
|
"Mod",
|
||||||
|
"Shift"
|
||||||
|
],
|
||||||
|
"key": "R"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
test-vault/.obsidian/plugins/obsidian-custom-frames/.gitignore
vendored
Normal file
3
test-vault/.obsidian/plugins/obsidian-custom-frames/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!data.json
|
49
test-vault/.obsidian/plugins/obsidian-custom-frames/data.json
vendored
Normal file
49
test-vault/.obsidian/plugins/obsidian-custom-frames/data.json
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"url": "https://forum.obsidian.md/",
|
||||||
|
"displayName": "Obsidian Forum",
|
||||||
|
"icon": "edit",
|
||||||
|
"hideOnMobile": true,
|
||||||
|
"addRibbonIcon": true,
|
||||||
|
"openInCenter": true,
|
||||||
|
"zoomLevel": 1,
|
||||||
|
"forceIframe": false,
|
||||||
|
"customCss": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://keep.google.com",
|
||||||
|
"displayName": "Google Keep",
|
||||||
|
"icon": "files",
|
||||||
|
"hideOnMobile": true,
|
||||||
|
"addRibbonIcon": false,
|
||||||
|
"openInCenter": false,
|
||||||
|
"zoomLevel": 1,
|
||||||
|
"forceIframe": false,
|
||||||
|
"customCss": "/* hide the menu bar, the \"Keep\" text and the Google Apps button */\nhtml > body > div:nth-child(2) > div:nth-child(2) > div:first-child, \nhtml > body > div:first-child > header:first-child > div > div:first-child > div > div:first-child > a:first-child > span, \nhtml > body > div:first-child > header:first-child > div:nth-child(2) > div:first-child > div:first-child, \nhtml > body > div:first-child > header:first-child > div:nth-child(2) > div:nth-child(3) > div:first-child > div:first-child > div:first-child { \n\tdisplay: none !important; \n}\nhtml > body > div:first-child > header:first-child > div > div:first-child > div > div:first-child > a:first-child {\n\tcursor: default; \n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://calendar.google.com/calendar",
|
||||||
|
"displayName": "Google Calendar",
|
||||||
|
"icon": "calendar",
|
||||||
|
"hideOnMobile": true,
|
||||||
|
"addRibbonIcon": true,
|
||||||
|
"openInCenter": true,
|
||||||
|
"zoomLevel": 1,
|
||||||
|
"forceIframe": false,
|
||||||
|
"customCss": "/* hide the menu bar, \"Keep\" text, and logo */\nhtml > body > div:nth-child(2) > div:nth-child(2) > div:first-child[class*=\" \"],\nhtml > body > div:first-child > header:first-child > div > div:first-child > div > div:first-child,\nhtml > body > div:nth-child(2) > div:nth-child(2) > div:first-child > div:first-child {\ndisplay: none !important;\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://scholar.google.com",
|
||||||
|
"displayName": "Scholar",
|
||||||
|
"icon": "book",
|
||||||
|
"hideOnMobile": false,
|
||||||
|
"addRibbonIcon": true,
|
||||||
|
"openInCenter": false,
|
||||||
|
"zoomLevel": 1,
|
||||||
|
"forceIframe": false,
|
||||||
|
"customCss": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"padding": 5
|
||||||
|
}
|
6
test-vault/Untitled.canvas
Normal file
6
test-vault/Untitled.canvas
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"nodes":[
|
||||||
|
{"id":"98d23d47aafe2b68","x":-480,"y":-360,"width":795,"height":535,"type":"link","url":"https://calendar.google.com"}
|
||||||
|
],
|
||||||
|
"edges":[]
|
||||||
|
}
|
0
test-vault/note.md
Normal file
0
test-vault/note.md
Normal file
|
@ -1,23 +1,23 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"inlineSourceMap": true,
|
"inlineSourceMap": true,
|
||||||
"inlineSources": true,
|
"inlineSources": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"target": "ES6",
|
"target": "ES6",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"DOM",
|
"DOM",
|
||||||
"ES5",
|
"ES5",
|
||||||
"ES6",
|
"ES6",
|
||||||
"ES7"
|
"ES7"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts"
|
"**/*.ts"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
{
|
{
|
||||||
"2.0.0": "0.13.33",
|
"2.0.0": "0.13.33",
|
||||||
"2.0.1": "0.13.33",
|
"2.0.1": "0.13.33",
|
||||||
"2.1.0": "0.13.33",
|
"2.1.0": "0.13.33",
|
||||||
"2.2.0": "0.14.3",
|
"2.2.0": "0.14.3",
|
||||||
"2.2.1": "0.14.3",
|
"2.2.1": "0.14.3",
|
||||||
"2.2.2": "0.14.3",
|
"2.2.2": "0.14.3",
|
||||||
"2.3.0": "0.14.5",
|
"2.3.0": "0.14.5",
|
||||||
"2.4.0": "0.14.5",
|
"2.4.0": "0.14.5",
|
||||||
"2.4.1": "0.14.5",
|
"2.4.1": "0.14.5",
|
||||||
"2.4.2": "0.14.5"
|
"2.4.2": "0.14.5",
|
||||||
}
|
"2.4.3": "0.15.5",
|
||||||
|
"2.4.4": "0.16.0",
|
||||||
|
"2.4.5": "1.1.0",
|
||||||
|
"2.4.6": "1.2.0",
|
||||||
|
"2.4.7": "1.2.0"
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue