Files
BlogPosts/.obsidian/plugins/obsidian-custom-frames/main.js
2026-01-22 20:10:59 +08:00

657 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __reExport = (target, module2, desc) => {
if (module2 && typeof module2 === "object" || typeof module2 === "function") {
for (let key of __getOwnPropNames(module2))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
}
return target;
};
var __toModule = (module2) => {
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
};
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/main.ts
__export(exports, {
default: () => CustomFramesPlugin
});
var import_obsidian4 = __toModule(require("obsidian"));
// src/frame.ts
var import_obsidian = __toModule(require("obsidian"));
// src/settings.ts
var defaultSettings = {
frames: [],
padding: 5
};
var presets = {
"obsidian": {
url: "https://forum.obsidian.md/",
displayName: "Obsidian Forum",
icon: "edit",
hideOnMobile: true,
addRibbonIcon: true,
openInCenter: true,
zoomLevel: 1,
forceIframe: false,
customCss: "",
customJs: ""
},
"detexify": {
url: "https://detexify.kirelabs.org/classify.html",
displayName: "Detexify",
icon: "type",
hideOnMobile: true,
addRibbonIcon: true,
openInCenter: false,
zoomLevel: 0.95,
forceIframe: false,
customCss: `/* hide info clutter and ad banner */
#classify--info-area,
.adsbygoogle {
display: none !important
}`,
customJs: ""
},
"calendar": {
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 "Calendar" text and remove minimum width */
div[style*="min-width: 238px"] {
min-width: 0 !important;
padding-right: 0 !important;
}
div[style*="min-width: 238px"] span[role*="heading"] {
display: none !important;
}`,
customJs: ""
},
"keep": {
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 */
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: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": {
url: "https://todoist.com",
displayName: "Todoist",
icon: "list-checks",
hideOnMobile: true,
addRibbonIcon: false,
openInCenter: false,
zoomLevel: 1,
forceIframe: false,
customCss: `/* hide the help, home, search, and productivity overview buttons, create extra space, and prevent toast pop-up from acting weird */
[aria-label="Go to Home view"], #quick_find, [aria-label="Productivity"], [aria-label="Help & Feedback"] {
display: none !important;
}
.view_content {
padding-left: 15px;
}
.view_header {
padding-left: 15px;
padding-top: 10px;
}
.undo_toast {
width: 95%;
}`,
customJs: ""
},
"notion": {
url: "https://www.notion.so/",
displayName: "Notion",
icon: "box",
hideOnMobile: true,
addRibbonIcon: true,
openInCenter: true,
zoomLevel: 1,
forceIframe: false,
customCss: "",
customJs: ""
},
"twitter": {
url: "https://twitter.com",
displayName: "Twitter",
icon: "twitter",
hideOnMobile: true,
addRibbonIcon: false,
openInCenter: false,
zoomLevel: 1,
forceIframe: false,
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: ""
},
"readwise-daily-review": {
"url": "https://readwise.io/dailyreview",
"displayName": "Readwise Daily Review",
"icon": "highlighter",
"hideOnMobile": true,
"addRibbonIcon": false,
"openInCenter": false,
"zoomLevel": 1,
"forceIframe": false,
"customCss": ".fixed-nav {\n display: none !important;\n}",
"customJs": ""
}
};
function getIcon(settings) {
return settings.icon ? `lucide-${settings.icon}` : "documents";
}
function getId(settings) {
return settings.displayName.toLowerCase().replace(/\s/g, "-");
}
// src/frame.ts
var CustomFrame = class {
constructor(settings, data) {
this.settings = settings;
this.data = data;
}
create(parent, additionalStyle = void 0, urlSuffix = void 0) {
let style = `padding: ${this.settings.padding}px;`;
if (additionalStyle)
style += additionalStyle;
if (import_obsidian.Platform.isDesktopApp && !this.data.forceIframe) {
let frameDoc = parent.doc;
this.frame = frameDoc.createElement("webview");
this.frame.partition = "persist:vault-" + app.appId;
parent.appendChild(this.frame);
this.frame.setAttribute("allowpopups", "");
this.frame.addEventListener("dom-ready", () => {
this.frame.setZoomFactor(this.data.zoomLevel);
this.frame.insertCSS(this.data.customCss);
this.frame.executeJavaScript(this.data.customJs);
});
this.frame.addEventListener("destroyed", () => {
if (frameDoc != parent.doc) {
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;");
style += `transform: scale(${this.data.zoomLevel}); transform-origin: 0 0;`;
}
this.frame.addClass("custom-frames-frame");
this.frame.addClass(`custom-frames-${getId(this.data)}`);
this.frame.setAttribute("style", style);
let src = new URL(this.data.url);
if (urlSuffix) {
let suffix = new URL(urlSuffix, src.origin);
suffix.searchParams.forEach((value, key) => {
src.searchParams.set(key, value);
});
if (suffix.pathname !== "/") {
src.pathname += suffix.pathname;
}
src.hash = suffix.hash || src.hash;
}
this.frame.setAttribute("src", src.toString());
}
refresh() {
if (this.frame instanceof HTMLIFrameElement) {
this.frame.contentWindow.location.reload();
} else {
this.frame.reload();
}
}
return() {
if (this.frame instanceof HTMLIFrameElement) {
this.frame.contentWindow.open(this.data.url);
} else {
this.frame.loadURL(this.data.url);
}
}
goBack() {
if (this.frame instanceof HTMLIFrameElement) {
this.frame.contentWindow.history.back();
} else {
this.frame.goBack();
}
}
goForward() {
if (this.frame instanceof HTMLIFrameElement) {
this.frame.contentWindow.history.forward();
} else {
this.frame.goForward();
}
}
toggleDevTools() {
if (!(this.frame instanceof HTMLIFrameElement)) {
if (!this.frame.isDevToolsOpened()) {
this.frame.openDevTools();
} else {
this.frame.closeDevTools();
}
}
}
getCurrentUrl() {
return this.frame instanceof HTMLIFrameElement ? this.frame.contentWindow.location.href : this.frame.getURL();
}
focus() {
if (this.frame instanceof HTMLIFrameElement) {
this.frame.contentWindow.focus();
} else {
this.frame.focus();
}
}
};
// src/settings-tab.ts
var import_obsidian2 = __toModule(require("obsidian"));
var CustomFramesSettingTab = class extends import_obsidian2.PluginSettingTab {
constructor(app2, plugin) {
super(app2, plugin);
this.plugin = plugin;
}
display() {
this.containerEl.empty();
this.containerEl.createEl("h2", { text: "自定义标签页设置" });
this.containerEl.createEl("p", {
text: "请注意大多数设置需要重启或重新加载Obsidian才能生效。",
cls: "mod-warning"
});
new import_obsidian2.Setting(this.containerEl).setName("标签页内边距").setDesc("自定义标签页内部周围应留的内边距,单位为像素。").addText((t) => {
t.inputEl.type = "number";
t.setValue(String(this.plugin.settings.padding));
t.onChange((v) => __async(this, null, function* () {
this.plugin.settings.padding = v.length ? Number(v) : defaultSettings.padding;
yield this.plugin.saveSettings();
}));
});
for (let frame of this.plugin.settings.frames) {
let heading = this.containerEl.createEl("h3", { text: frame.displayName || "Unnamed Frame" });
let toggle = new import_obsidian2.ButtonComponent(this.containerEl).setButtonText("显示设置").setClass("custom-frames-show").onClick(() => __async(this, null, function* () {
content.hidden = !content.hidden;
toggle.setButtonText(content.hidden ? "Show Settings" : "Hide Settings");
}));
let content = this.containerEl.createDiv();
content.hidden = true;
new import_obsidian2.Setting(content).setName("显示名称").setDesc("此标签页应具有的显示名称。").addText((t) => {
t.setValue(frame.displayName);
t.onChange((v) => __async(this, null, function* () {
frame.displayName = v;
heading.setText(frame.displayName || "Unnamed Frame");
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("图标").setDesc(createFragment((f) => {
f.createSpan({ text: "此标签页应具有的图标。可以使用任何 " });
f.createEl("a", { text: "Lucide 图标", href: "https://lucide.dev/" });
f.createSpan({ text: " 的名称。" });
})).addText((t) => {
t.setValue(frame.icon);
t.onChange((v) => __async(this, null, function* () {
frame.icon = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("URL").setDesc("此标签页中应打开的URL。").addText((t) => {
t.setValue(frame.url);
t.onChange((v) => __async(this, null, function* () {
frame.url = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("在移动设备上禁用").setDesc("自定义标签页在移动设备上受到更多限制,不支持显示相同类型的内容。如果某个标签页在移动设备上无法正常工作,可以将其禁用。").addToggle((t) => {
t.setValue(frame.hideOnMobile);
t.onChange((v) => __async(this, null, function* () {
frame.hideOnMobile = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("添加侧边栏图标").setDesc("是否应在侧边栏添加一个按钮来打开此标签页。").addToggle((t) => {
t.setValue(frame.addRibbonIcon);
t.onChange((v) => __async(this, null, function* () {
frame.addRibbonIcon = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("在中间打开").setDesc("此标签页是否应在未固定的中心编辑器中打开,而不是在侧边栏中打开。这对于在狭窄视图下效果不佳的网站,或在查看时不需要打开笔记的网站很有用。").addToggle((t) => {
t.setValue(frame.openInCenter);
t.onChange((v) => __async(this, null, function* () {
frame.openInCenter = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("强制使用iframe").setDesc(createFragment((f) => {
f.createSpan({ text: "此标签页在桌面端是使用iframe还是Electron webviews。" });
f.createEl("br");
f.createEl("em", { text: "仅当此标签页导致问题或频繁崩溃时才启用此设置。此设置会导致所有仅限桌面端的设置被忽略。" });
})).addToggle((t) => {
t.setValue(frame.forceIframe);
t.onChange((v) => __async(this, null, function* () {
frame.forceIframe = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("页面缩放").setDesc("此标签页页面应显示的缩放比例,以百分比表示。").addText((t) => {
t.inputEl.type = "number";
t.setValue(String(frame.zoomLevel * 100));
t.onChange((v) => __async(this, null, function* () {
frame.zoomLevel = v.length ? Number(v) / 100 : 1;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("额外CSS").setDesc(createFragment((f) => {
f.createSpan({ text: "应应用于此标签页的额外CSS代码片段。" });
f.createEl("br");
f.createEl("em", { text: "请注意,这仅适用于桌面端。" });
})).addTextArea((t) => {
t.inputEl.rows = 5;
t.inputEl.cols = 50;
t.setValue(frame.customCss);
t.onChange((v) => __async(this, null, function* () {
frame.customCss = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.Setting(content).setName("额外JavaScript").setDesc(createFragment((f) => {
f.createSpan({ text: "应应用于此标签页的额外JavaScript代码片段。" });
f.createEl("br");
f.createEl("em", { text: "请注意,这仅适用于桌面端。" });
})).addTextArea((t) => {
t.inputEl.rows = 5;
t.inputEl.cols = 50;
t.setValue(frame.customJs);
t.onChange((v) => __async(this, null, function* () {
frame.customJs = v;
yield this.plugin.saveSettings();
}));
});
new import_obsidian2.ButtonComponent(content).setButtonText("移除标签页").onClick(() => __async(this, null, function* () {
this.plugin.settings.frames.remove(frame);
yield this.plugin.saveSettings();
this.display();
}));
}
this.containerEl.createEl("hr");
this.containerEl.createEl("p", { text: `创建一个新标签页,可以从插件自带的预设中选择,也可以自定义编辑。每个标签页的标签页都可以通过“自定义标签页:打开”命令打开。` });
let addDiv = this.containerEl.createDiv();
let dropdown = new import_obsidian2.DropdownComponent(addDiv);
dropdown.addOption("new", "Custom");
for (let [key, value] of Object.entries(presets).sort((a, b) => a[1].displayName.localeCompare(b[1].displayName)))
dropdown.addOption(key, value.displayName);
new import_obsidian2.ButtonComponent(addDiv).setButtonText("添加标签页").setClass("custom-frames-add").onClick(() => __async(this, null, function* () {
let option = dropdown.getValue();
if (option == "new") {
this.plugin.settings.frames.push({
url: "",
displayName: "New Frame",
icon: "",
hideOnMobile: true,
addRibbonIcon: false,
openInCenter: false,
zoomLevel: 1,
forceIframe: false,
customCss: "",
customJs: ""
});
} else {
this.plugin.settings.frames.push(presets[option]);
}
yield this.plugin.saveSettings();
this.display();
}));
let disclaimer = this.containerEl.createEl("p", { cls: "mod-warning" });
disclaimer.createSpan({ text: "请注意,当您将网站添加为自定义标签页时,您输入的个人信息可能会暴露给您安装的其他插件。有关更多信息,请参阅 " });
disclaimer.createEl("a", {
text: "此讨论",
href: "https://github.com/Ellpeck/ObsidianCustomFrames/issues/54#issuecomment-1210879685",
cls: "mod-warning"
});
disclaimer.createSpan({ text: "。" });
this.containerEl.createEl("hr");
this.containerEl.createEl("p", { text: "需要插件使用帮助欢迎加入Discord服务器" });
this.containerEl.createEl("a", { href: "https://link.ellpeck.de/discordweb" }).createEl("img", {
attr: { src: "https://ellpeck.de/res/discord-wide.png" },
cls: "custom-frames-support"
});
this.containerEl.createEl("p", { text: "如果您喜欢此插件并希望支持其开发,可以通过点击此精美图片访问我的网站进行支持!" });
this.containerEl.createEl("a", { href: "https://ellpeck.de/support" }).createEl("img", {
attr: { src: "https://ellpeck.de/res/generalsupport-wide.png" },
cls: "custom-frames-support"
});
}
};
// src/view.ts
var import_obsidian3 = __toModule(require("obsidian"));
var _CustomFrameView = class extends import_obsidian3.ItemView {
constructor(leaf, settings, data, name) {
super(leaf);
this.data = data;
this.name = name;
this.frame = new CustomFrame(settings, data);
this.navigation = data.openInCenter;
for (let action of _CustomFrameView.actions)
this.addAction(action.icon, action.name, () => action.action(this));
}
onload() {
this.contentEl.empty();
this.contentEl.addClass("custom-frames-view");
this.frame.create(this.contentEl);
}
onPaneMenu(menu, source) {
super.onPaneMenu(menu, source);
for (let action of _CustomFrameView.actions) {
menu.addItem((i) => {
i.setTitle(action.name);
i.setIcon(action.icon);
i.onClick(() => action.action(this));
});
}
}
getViewType() {
return this.name;
}
getDisplayText() {
return this.data.displayName;
}
getIcon() {
return getIcon(this.data);
}
focus() {
this.frame.focus();
}
};
var CustomFrameView = _CustomFrameView;
CustomFrameView.actions = [
{
name: "返回原始页面",
icon: "home",
action: (v) => v.frame.return()
},
{
name: "打开开发者工具",
icon: "binary",
action: (v) => v.frame.toggleDevTools()
},
{
name: "复制链接",
icon: "link",
action: (v) => navigator.clipboard.writeText(v.frame.getCurrentUrl())
},
{
name: "在浏览器中打开",
icon: "globe",
action: (v) => open(v.frame.getCurrentUrl())
},
{
name: "刷新",
icon: "refresh-cw",
action: (v) => v.frame.refresh()
},
{
name: "前进",
icon: "arrow-right",
action: (v) => v.frame.goForward()
},
{
name: "后退",
icon: "arrow-left",
action: (v) => v.frame.goBack()
}
];
// src/main.ts
var CustomFramesPlugin = class extends import_obsidian4.Plugin {
onload() {
return __async(this, null, function* () {
yield this.loadSettings();
for (let frame of this.settings.frames) {
if (!frame.url || !frame.displayName)
continue;
let name = `custom-frames-${getId(frame)}`;
if (import_obsidian4.Platform.isMobileApp && frame.hideOnMobile) {
console.log(`跳过在移动设备上隐藏的标签页 ${name}`);
continue;
}
try {
console.log(`为URL ${frame.url} 注册标签页 ${name}`);
this.registerView(name, (l) => new CustomFrameView(l, this.settings, frame, name));
this.addCommand({
id: `open-${name}`,
name: `打开 ${frame.displayName}`,
callback: () => this.openLeaf(name, frame.openInCenter, false)
});
if (frame.addRibbonIcon)
this.addRibbonIcon(getIcon(frame), `Open ${frame.displayName}`, (e) => this.openLeaf(name, frame.openInCenter, import_obsidian4.Platform.isMacOS ? e.metaKey : e.ctrlKey));
} catch (e) {
console.error(`无法注册标签页 ${name},是否已存在同名标签页?`);
}
}
this.addSettingTab(new CustomFramesSettingTab(this.app, this));
this.registerMarkdownCodeBlockProcessor("custom-frames", (s, e) => {
e.empty();
e.addClass("custom-frames-view-file");
let frameMatch = /frame:([^\n]+)/gi.exec(s);
let frameName = frameMatch && frameMatch[1].trim();
if (!frameName) {
e.createSpan({ text: "无法解析标签页名称" });
return;
}
let data = this.settings.frames.find((f) => f.displayName == frameName);
if (!data) {
e.createSpan({ text: `找不到名为 ${frameName} 的标签页` });
return;
}
if (import_obsidian4.Platform.isMobileApp && data.hideOnMobile) {
e.createSpan({ text: `${frameName} 在移动设备上隐藏` });
return;
}
let styleMatch = /style:([^\n]+)/gi.exec(s);
let style = styleMatch && styleMatch[1].trim();
style || (style = "height: 600px;");
let urlSuffixMatch = /urlsuffix:([^\n]+)/gi.exec(s);
let urlSuffix = urlSuffixMatch && urlSuffixMatch[1].trim();
urlSuffix || (urlSuffix = "");
let frame = new CustomFrame(this.settings, data);
frame.create(e, style, urlSuffix);
});
});
}
loadSettings() {
return __async(this, null, function* () {
this.settings = Object.assign({}, defaultSettings, yield this.loadData());
});
}
saveSettings() {
return __async(this, null, function* () {
yield this.saveData(this.settings);
});
}
openLeaf(name, center, split) {
return __async(this, null, function* () {
let leaf;
if (center) {
leaf = this.app.workspace.getLeaf(split);
yield leaf.setViewState({ type: name, active: true });
} else {
if (!this.app.workspace.getLeavesOfType(name).length)
yield this.app.workspace.getRightLeaf(false).setViewState({ type: name, active: true });
leaf = this.app.workspace.getLeavesOfType(name)[0];
this.app.workspace.revealLeaf(leaf);
}
if (leaf.view instanceof CustomFrameView)
leaf.view.focus();
});
}
};
/* nosourcemap */