From 7e90bb587fd86b32943d14fcd6104b52fb8fdb40 Mon Sep 17 00:00:00 2001 From: Mathis Gauthey Date: Thu, 4 Jan 2024 10:47:15 +0100 Subject: [PATCH] update: Add obsidian-scroll-to-top plugin --- .../obsidian-scroll-to-top-plugin/main.js | 439 ++++++++++++++++++ .../manifest.json | 10 + .../obsidian-scroll-to-top-plugin/styles.css | 214 +++++++++ 3 files changed, 663 insertions(+) create mode 100644 .obsidian/plugins/obsidian-scroll-to-top-plugin/main.js create mode 100644 .obsidian/plugins/obsidian-scroll-to-top-plugin/manifest.json create mode 100644 .obsidian/plugins/obsidian-scroll-to-top-plugin/styles.css diff --git a/.obsidian/plugins/obsidian-scroll-to-top-plugin/main.js b/.obsidian/plugins/obsidian-scroll-to-top-plugin/main.js new file mode 100644 index 0000000..2d8bd3b --- /dev/null +++ b/.obsidian/plugins/obsidian-scroll-to-top-plugin/main.js @@ -0,0 +1,439 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// main.ts +var main_exports = {}; +__export(main_exports, { + default: () => ScrollToTopPlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian2 = require("obsidian"); + +// src/command.ts +var addPluginCommand = (plugin, id, name, callback) => { + plugin.addCommand({ + id, + name, + callback + }); +}; + +// utils/index.ts +var isPreview = (markdownView) => { + const mode = markdownView.getMode(); + return mode === "preview"; +}; +var isSource = (markdownView) => { + const mode = markdownView.getMode(); + return mode === "source"; +}; + +// src/setting.ts +var import_obsidian = require("obsidian"); +var scrollToTopSetting = { + enabledScrollToTop: true, + enabledScrollToBottom: true, + enabledScrollToCursor: true, + iconScrollToTop: "arrow-up", + iconScrollToBottom: "arrow-down", + iconScrollToCursor: "text-cursor-input", + showTooltip: true, + scrollTopTooltipText: "Scroll to top", + scrollBottomTooltipText: "Scroll to bottom", + scrollCursorTooltipText: "Scroll to cursor position", + enableSurfingPlugin: false, + resizeButton: 1 +}; +var ScrollToTopSettingTab = class extends import_obsidian.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + createSpanWithLinks(text, href, linkText) { + const span = activeDocument.createElement("span"); + span.innerText = text; + const link = activeDocument.createElement("a"); + link.href = href; + link.innerText = linkText; + span.appendChild(link); + return span; + } + rebuildButton() { + this.plugin.removeButton("__C_scrollToTop"); + this.plugin.removeButton("__C_scrollToBottom"); + this.plugin.removeButton("__C_scrollToCursor"); + this.plugin.createButton(); + if (this.plugin.windowSet.size > 0) { + this.plugin.windowSet.forEach((window2) => { + this.plugin.removeButton("__C_scrollToTop", window2); + this.plugin.removeButton("__C_scrollToBottom", window2); + this.plugin.removeButton("__C_scrollToCursor", window2); + this.plugin.createButton(window2); + }); + } + } + display() { + const { containerEl } = this; + containerEl.empty(); + containerEl.createEl("h2", { text: "Scroll To Top Settings" }); + new import_obsidian.Setting(containerEl).setName("Show scroll to top button").setDesc("Show scroll to top button in the right bottom corner.").addToggle((value) => { + value.setValue(this.plugin.settings.enabledScrollToTop).onChange(async (value2) => { + this.plugin.settings.enabledScrollToTop = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Show scroll to bottom button").setDesc("Show scroll to bottom button in the right bottom corner.").addToggle((value) => { + value.setValue(this.plugin.settings.enabledScrollToBottom).onChange(async (value2) => { + this.plugin.settings.enabledScrollToBottom = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Show scroll to cursor button").setDesc("Show scroll to cursor button in the right bottom corner.").addToggle((value) => { + value.setValue(this.plugin.settings.enabledScrollToCursor).onChange(async (value2) => { + this.plugin.settings.enabledScrollToCursor = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Show Tooltip").setDesc("Show tooltip when hover on the button.").addToggle((value) => { + value.setValue(this.plugin.settings.showTooltip).onChange(async (value2) => { + this.plugin.settings.showTooltip = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Scroll on WebView (Beta)").setDesc("Scroll on WebView (Should work with Surfing Plugin).").addToggle((value) => { + value.setValue(this.plugin.settings.enableSurfingPlugin).onChange(async (value2) => { + this.plugin.settings.enableSurfingPlugin = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Resize buttons").setDesc("Change size of buttons.").addSlider((slider) => { + slider.setLimits(0.7, 1.4, 0.1).setValue(this.plugin.settings.resizeButton).setDynamicTooltip().onChange(async (value) => { + this.plugin.settings.resizeButton = value; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }).addExtraButton((btn) => { + btn.setIcon("reset").setTooltip("Reset to default").onClick(async () => { + this.plugin.settings.resizeButton = scrollToTopSetting.resizeButton; + await this.plugin.saveSettings(); + this.rebuildButton(); + this.display(); + }); + }); + new import_obsidian.Setting(containerEl).setName("tooltip config for top button").setDesc("Change tooltip text of scroll to top button.").addText((value) => { + value.setValue(this.plugin.settings.scrollTopTooltipText).onChange(async (value2) => { + this.plugin.settings.scrollTopTooltipText = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("tooltip config for bottom button").setDesc("Change tooltip text of scroll to bottom button.").addText((value) => { + value.setValue(this.plugin.settings.scrollBottomTooltipText).onChange(async (value2) => { + this.plugin.settings.scrollBottomTooltipText = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("tooltip config for cursor button").setDesc("Change tooltip text of scroll to cursor button.").addText((value) => { + value.setValue(this.plugin.settings.scrollCursorTooltipText).onChange(async (value2) => { + this.plugin.settings.scrollCursorTooltipText = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Change icon of scroll to top button").setDesc(this.createSpanWithLinks("Change icon of scroll to top button. You can visit available icons here: ", "https://lucide.dev/", "lucide.dev")).addText((value) => { + value.setValue(this.plugin.settings.iconScrollToTop).onChange(async (value2) => { + this.plugin.settings.iconScrollToTop = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Change icon of scroll to bottom button").setDesc(this.createSpanWithLinks("Change icon of scroll to bottom button. You can visit available icons here: ", "https://lucide.dev/", "lucide.dev")).addText((value) => { + value.setValue(this.plugin.settings.iconScrollToBottom).onChange(async (value2) => { + this.plugin.settings.iconScrollToBottom = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + new import_obsidian.Setting(containerEl).setName("Change icon of scroll to cursor button").setDesc(this.createSpanWithLinks("Change icon of scroll to cursor button. You can visit available icons here: ", "https://lucide.dev/", "lucide.dev")).addText((value) => { + value.setValue(this.plugin.settings.iconScrollToCursor).onChange(async (value2) => { + this.plugin.settings.iconScrollToCursor = value2; + await this.plugin.saveSettings(); + this.rebuildButton(); + }); + }); + } +}; + +// plugins/surfing.ts +var isContainSurfingWebview = (settings) => { + return settings.enableSurfingPlugin && (activeDocument.querySelector(".wb-frame") || activeDocument.querySelector(".wb-page-search-bar")); +}; +var injectSurfingComponent = (top = true) => { + const webViewList = activeDocument.querySelectorAll("webview"); + const webView = Array.from(webViewList).find((item) => { + var _a; + const workspaceLeafElem = item.parentElement.parentElement.parentElement; + const display = (_a = workspaceLeafElem.style) == null ? void 0 : _a.display; + const modActive = workspaceLeafElem.classList.contains("mod-active"); + return display != "none" && modActive; + }); + if (webView) { + if (top) { + webView.executeJavaScript(`window.scrollTo(0,0)`); + } else { + webView.executeJavaScript(`window.scrollTo(0,document.body.scrollHeight)`); + } + } +}; + +// main.ts +var ROOT_WORKSPACE_CLASS = ".mod-vertical.mod-root"; +var globalMarkdownView = null; +var ScrollToTopPlugin = class extends import_obsidian2.Plugin { + constructor() { + super(...arguments); + this.windowSet = /* @__PURE__ */ new Set(); + this.scrollToBottom = async () => { + const markdownView = this.getCurrentViewOfType(); + if (markdownView) { + const file = this.app.workspace.getActiveFile(); + const content = await this.app.vault.cachedRead(file); + const lines = content.split("\n"); + let numberOfLines = lines.length; + if (markdownView.getMode() === "preview") { + while (numberOfLines > 0 && lines[numberOfLines - 1].trim() === "") { + numberOfLines--; + } + } + markdownView.currentMode.applyScroll(numberOfLines - 1); + } else if (isContainSurfingWebview(this.settings)) { + injectSurfingComponent(false); + } + }; + } + scrollToCursor() { + const markdownView = this.getCurrentViewOfType(); + if (markdownView) { + const editor = markdownView.editor; + const anchor = editor.getCursor("anchor"); + const head = editor.getCursor("head"); + setTimeout(async () => { + editor.setSelection(anchor, head); + }, 200); + editor.scrollIntoView({ + from: anchor, + to: head + }, true); + this.app.workspace.setActiveLeaf(markdownView.leaf, { + focus: true + }); + } else if (isContainSurfingWebview(this.settings)) { + injectSurfingComponent(false); + } + } + scrollToTop() { + const markdownView = this.getCurrentViewOfType(); + if (markdownView) { + const preview = markdownView.previewMode; + if (isSource(markdownView)) { + const editor = markdownView.editor; + setTimeout(async () => { + editor.setCursor(0, 0); + }, 200); + editor.scrollTo(0, 0); + this.app.workspace.setActiveLeaf(markdownView.leaf, { + focus: true + }); + } else { + isPreview(markdownView) && preview.applyScroll(0); + } + } else if (isContainSurfingWebview(this.settings)) { + injectSurfingComponent(true); + } + } + createScrollElement(config, fn) { + var _a; + let topWidget = createEl("div"); + topWidget.setAttribute("class", `div-${config.className}`); + topWidget.setAttribute("id", config.id); + document.body.style.setProperty("--size-ratio", this.settings.resizeButton.toString()); + let button = new import_obsidian2.ButtonComponent(topWidget); + button.setIcon(config.icon).setClass("buttonItem").onClick(fn); + if (config.tooltipConfig.showTooltip) { + button.setTooltip(config.tooltipConfig.tooltipText); + } + let curWindow = config.curWindow || window; + const markdownView = this.getCurrentViewOfType(); + (_a = curWindow.document.body.querySelector(ROOT_WORKSPACE_CLASS)) == null ? void 0 : _a.insertAdjacentElement("afterbegin", topWidget); + if (!markdownView && !isContainSurfingWebview(this.settings)) { + topWidget.style.visibility = "hidden"; + } + } + removeButton(id, curWindow) { + let curWin = curWindow || window; + const element = curWin.activeDocument.getElementById(id); + if (element) { + element.remove(); + } + } + getCurrentViewOfType() { + var _a; + let markdownView = this.app.workspace.getActiveViewOfType(import_obsidian2.MarkdownView); + let currentView = this.app.workspace.getActiveViewOfType(import_obsidian2.View); + if (markdownView !== null) { + globalMarkdownView = markdownView; + } else { + if (currentView == null || ((_a = currentView == null ? void 0 : currentView.file) == null ? void 0 : _a.extension) == "md") { + markdownView = globalMarkdownView; + } + } + return markdownView; + } + createButton(window2) { + const { + enabledScrollToTop, + enabledScrollToBottom, + enabledScrollToCursor, + iconScrollToTop, + iconScrollToBottom, + iconScrollToCursor, + showTooltip, + scrollTopTooltipText, + scrollBottomTooltipText, + scrollCursorTooltipText + } = this.settings; + if (enabledScrollToTop) { + this.createScrollElement({ + id: "__C_scrollToTop", + className: "scrollToTop", + icon: iconScrollToTop, + curWindow: window2, + tooltipConfig: { + showTooltip, + tooltipText: scrollTopTooltipText + } + }, this.scrollToTop.bind(this)); + } + if (enabledScrollToBottom) { + this.createScrollElement({ + id: "__C_scrollToBottom", + className: "scrollToBottom", + icon: iconScrollToBottom, + curWindow: window2, + tooltipConfig: { + showTooltip, + tooltipText: scrollBottomTooltipText + } + }, this.scrollToBottom.bind(this)); + } + if (enabledScrollToCursor) { + this.createScrollElement({ + id: "__C_scrollToCursor", + className: "scrollToCursor", + icon: iconScrollToCursor, + curWindow: window2, + tooltipConfig: { + showTooltip, + tooltipText: scrollCursorTooltipText + } + }, this.scrollToCursor.bind(this)); + } + } + toggleIconView() { + let BottomButton = activeDocument.querySelector(".div-scrollToBottom"); + let TopButton = activeDocument.querySelector(".div-scrollToTop"); + let CursorButton = activeDocument.querySelector(".div-scrollToCursor"); + const markdownView = this.getCurrentViewOfType(); + if (!markdownView && !isContainSurfingWebview(this.settings)) { + if (BottomButton) + BottomButton.style.visibility = "hidden"; + if (TopButton) + TopButton.style.visibility = "hidden"; + if (CursorButton) + CursorButton.style.visibility = "hidden"; + } else { + if (BottomButton) + BottomButton.style.visibility = "visible"; + if (TopButton) + TopButton.style.visibility = "visible"; + if (markdownView && isSource(markdownView)) { + if (CursorButton) + CursorButton.style.visibility = "visible"; + } else { + if (CursorButton) + CursorButton.style.visibility = "hidden"; + } + } + } + async onload() { + await this.loadSettings(); + this.addSettingTab(new ScrollToTopSettingTab(this.app, this)); + this.app.workspace.onLayoutReady(() => { + this.createButton(); + this.registerEvent(this.app.workspace.on("file-open", () => { + this.toggleIconView(); + })); + this.registerEvent(this.app.workspace.on("window-open", (win, window2) => { + this.windowSet.add(window2); + this.createButton(window2); + this.toggleIconView(); + })); + this.registerEvent(this.app.workspace.on("window-close", (win, window2) => { + this.windowSet.delete(window2); + })); + this.registerEvent(this.app.workspace.on("layout-change", () => { + this.toggleIconView(); + })); + }); + addPluginCommand(this, "scroll-to-top", "Scroll to Top", this.scrollToTop.bind(this)); + addPluginCommand(this, "scroll-to-bottom", "Scroll to Bottom", this.scrollToBottom.bind(this)); + addPluginCommand(this, "scroll-to-cursor", "Scroll to Cursor", this.scrollToCursor.bind(this)); + setTimeout(() => { + this.app.workspace.trigger("css-change"); + }, 300); + } + async saveSettings() { + await this.saveData(this.settings); + } + async loadSettings() { + this.settings = Object.assign({}, scrollToTopSetting, await this.loadData()); + } + onunload() { + this.removeButton("__C_scrollToTop"); + this.removeButton("__C_scrollToBottom"); + this.removeButton("__C_scrollToCursor"); + if (this.windowSet.size > 0) { + this.windowSet.forEach((window2) => { + this.removeButton("__C_scrollToTop", window2); + this.removeButton("__C_scrollToBottom", window2); + this.removeButton("__C_scrollToCursor", window2); + }); + } + } +}; diff --git a/.obsidian/plugins/obsidian-scroll-to-top-plugin/manifest.json b/.obsidian/plugins/obsidian-scroll-to-top-plugin/manifest.json new file mode 100644 index 0000000..cf6c487 --- /dev/null +++ b/.obsidian/plugins/obsidian-scroll-to-top-plugin/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "obsidian-scroll-to-top-plugin", + "name": "Scroll to Top Plugin", + "version": "2.1.3", + "minAppVersion": "0.15.0", + "description": "This is a plugin for Obsidian that adds a button to scroll to the top of the current note.", + "author": "cloudhao1999", + "authorUrl": "https://github.com/cloudhao1999/obsidian-scroll-to-top-plugin", + "isDesktopOnly": false +} diff --git a/.obsidian/plugins/obsidian-scroll-to-top-plugin/styles.css b/.obsidian/plugins/obsidian-scroll-to-top-plugin/styles.css new file mode 100644 index 0000000..1b454a6 --- /dev/null +++ b/.obsidian/plugins/obsidian-scroll-to-top-plugin/styles.css @@ -0,0 +1,214 @@ +/* @settings +name: Scroll to Top Plugin +id: obsidian-scroll-to-top-plugin +settings: + - + id: scroll-to-top-bottom + title: Scroll to top bottom position placement + type: variable-number + default: 2.65 + format: em + - + id: scroll-to-top-left + title: Scroll to top right position placement + type: variable-number + default: 2.05 + format: em + - + id: scroll-to-bottom-bottom + title: Scroll to bottom bottom position placement + type: variable-number + default: 5.75 + format: em + - + id: scroll-to-bottom-left + title: Scroll to bottom right position placement + type: variable-number + default: 2.05 + format: em + - + id: scroll-to-cursor-bottom + title: Scroll to cursor bottom position placement + type: variable-number + default: 15.05 + format: em + - + id: scroll-to-cursor-left + title: Scroll to cursor right position placement + type: variable-number + default: 2.05 + format: em + - + id: scroll-to-top-width + title: Scroll to top button width + type: variable-number + default: 1.875 + format: em + - + id: scroll-to-top-height + title: Scroll to top button height + type: variable-number + default: 1.875 + format: em + - + id: scroll-to-bottom-width + title: Scroll to bottom button width + type: variable-number + default: 1.875 + format: em + - + id: scroll-to-bottom-height + title: Scroll to bottom button height + type: variable-number + default: 1.875 + format: em + - + id: scroll-to-cursor-width + title: Scroll to cursor button width + type: variable-number + default: 1.875 + format: em + - + id: scroll-to-cursor-height + title: Scroll to cursor button height + type: variable-number + default: 1.875 + format: em + +*/ + +/* In case not using the style settings plugin */ +:root { + --size-ratio: 1; + --scroll-to-cursor-bottom: 15.05em; + --scroll-to-cursor-left: 2.05em; + --scroll-to-bottom-bottom: 2.65em; + --scroll-to-bottom-left: 2.05em; + --scroll-to-top-bottom: 5.75em; + --scroll-to-top-left: 2.05em; + --scroll-input-width: 1.875em; + + --scroll-to-top-width: var(--scroll-input-width); + --scroll-to-top-height: var(--scroll-input-width); + --scroll-to-bottom-width: var(--scroll-input-width); + --scroll-to-bottom-height: var(--scroll-input-width); + --scroll-to-cursor-width: var(--scroll-input-width); + --scroll-to-cursor-height: var(--scroll-input-width); +} + +.div-scrollToTop { + position: absolute; + bottom: calc(var(--scroll-to-top-bottom) * var(--size-ratio)); + right: calc(var(--scroll-to-top-left) * var(--size-ratio)); + z-index: 99; +} + +.div-scrollToBottom { + position: absolute; + bottom: calc(var(--scroll-to-bottom-bottom) * var(--size-ratio)); + right: calc(var(--scroll-to-bottom-left) * var(--size-ratio)); + z-index: 99; +} + +.div-scrollToCursor { + position: absolute; + bottom: calc(var(--scroll-to-cursor-bottom) * var(--size-ratio)); + right: calc(var(--scroll-to-cursor-left) * var(--size-ratio)); + z-index: 99; +} + +#__C_scrollToTop { + width: auto; + height: auto; + padding: 3px; + display: grid; + user-select: none; + border-radius: 6px; + transition: 200ms ease; + min-width: fit-content; + justify-content: space-around; + z-index: var(--layer-status-bar); + box-shadow: 0px 3px 32px rgb(31 38 135 / 15%); + border: 1px solid var(--background-modifier-border); +} +#__C_scrollToTop .buttonItem { + margin: 2px; + border: none; + cursor: pointer; + padding: 5px 6px; + box-shadow: none; + margin-left: 3px; + margin-right: 3px; + border-radius: 3px; + width: calc(var(--scroll-to-top-width) * var(--size-ratio)); + height: calc(var(--scroll-to-top-height) * var(--size-ratio)); + font-size: initial !important; + background-color: var(--background-primary-alt); +} + +#__C_scrollToTop button.buttonItem:hover { + background-color: var(--background-secondary); +} + +#__C_scrollToBottom { + width: auto; + height: auto; + padding: 3px; + display: grid; + user-select: none; + border-radius: 6px; + transition: 200ms ease; + min-width: fit-content; + justify-content: space-around; + z-index: var(--layer-status-bar); + box-shadow: 0px 3px 32px rgb(31 38 135 / 15%); + border: 1px solid var(--background-modifier-border); +} +#__C_scrollToBottom .buttonItem { + margin: 2px; + border: none; + cursor: pointer; + padding: 5px 6px; + box-shadow: none; + margin-left: 3px; + margin-right: 3px; + width: calc(var(--scroll-to-bottom-width) * var(--size-ratio)); + height: calc(var(--scroll-to-bottom-height) * var(--size-ratio)); + border-radius: 3px; + font-size: initial !important; + background-color: var(--background-primary-alt); +} + +#__C_scrollToCursor { + width: auto; + height: auto; + padding: 3px; + display: grid; + user-select: none; + border-radius: 6px; + transition: 200ms ease; + min-width: fit-content; + justify-content: space-around; + z-index: var(--layer-status-bar); + box-shadow: 0px 3px 32px rgb(31 38 135 / 15%); + border: 1px solid var(--background-modifier-border); +} + +#__C_scrollToCursor .buttonItem { + margin: 2px; + border: none; + cursor: pointer; + padding: 5px 6px; + box-shadow: none; + margin-left: 3px; + margin-right: 3px; + width: calc(var(--scroll-to-cursor-width) * var(--size-ratio)); + height: calc(var(--scroll-to-cursor-height) * var(--size-ratio)); + border-radius: 3px; + font-size: initial !important; + background-color: var(--background-primary-alt); +} + +#__C_scrollToBottom button.buttonItem:hover { + background-color: var(--background-secondary); +}