commit 3c2c0f7ba63c32802cc64886b53169b168741cd3 Author: Panagiotis Koletsos <5904868+pcoletsos@users.noreply.github.com> Date: Sat Jun 13 17:39:16 2026 +0300 feat: scaffold dynamic wide content plugin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09772f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.DS_Store +*.log +.obsidian/ +data.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6dbfb39 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Panagiotis Koletsos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..be97198 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +# Dynamic Wide Content + +**Wide tables, diagrams, images, and code blocks without widening your prose.** + +Dynamic Wide Content fixes a common Obsidian annoyance: wide material gets +squeezed, clipped, or forced into awkward wrapping by the readable line length. + +The plugin keeps your prose exactly where it belongs: in Obsidian's comfortable +reading column. Only genuinely wide blocks break out into a wider scrollable +frame. + +## What It Does + +- Preserves Obsidian's normal readable prose width. +- Expands only wide Markdown tables, code blocks, Mermaid diagrams, PlantUML + diagrams, and image embeds. +- Adds horizontal scrolling inside the widened block frame when content is too + wide for the pane. +- Works in Reading view and Live Preview. +- Does not require disabling readable line length globally. + +## Why This Exists + +Readable line length is excellent for paragraphs, headings, lists, and normal +notes. It is painful for wide content: + +- pipeline or comparison tables +- architecture diagrams +- generated Mermaid and PlantUML output +- screenshots and reference images +- long code or log blocks + +Dynamic Wide Content makes those blocks usable without turning every note into +a full-width wall of text. + +## Settings + +- **Maximum wide content width**: Controls how far wide blocks may expand. +- **Viewport side margin**: Keeps widened blocks away from the edge of the + Obsidian pane. +- **Widen tables**: Allows Markdown tables to break out of the prose column. +- **Widen diagrams**: Allows Mermaid and PlantUML diagrams to use a wider frame. +- **Widen images**: Allows embedded images to display wider than prose. +- **Widen code blocks**: Allows preformatted code blocks to use a wider frame. +- **Keep table cells on one line**: Prevents wide tables from turning into tall, + wrapped rows. +- **Reading view support**: Applies widening in Reading view. +- **Live Preview support**: Applies widening in Live Preview rendered embeds. + +## Manual Installation + +1. Download `manifest.json`, `main.js`, and `styles.css` from a release. +2. Create this folder in your vault: + + ```text + VaultFolder/.obsidian/plugins/dynamic-wide-content/ + ``` + +3. Copy the three files into that folder. +4. Reload Obsidian. +5. Enable **Dynamic Wide Content** in Community Plugins. + +## Development + +This repository follows the standard Obsidian plugin shape. + +```bash +npm install +npm run dev +``` + +For release builds: + +```bash +npm run build +``` + +Community plugin releases should include: + +- `manifest.json` +- `main.js` +- `styles.css` + +## Privacy + +Dynamic Wide Content is a local UI plugin. It does not read note content, +contact any network service, collect analytics, or write files. + +## License + +MIT diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..bf321ca --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,43 @@ +import esbuild from "esbuild"; +import process from "process"; +import builtins from "builtin-modules"; + +const production = process.argv[2] === "production"; + +const context = await esbuild.context({ + banner: { + js: "/* Dynamic Wide Content */" + }, + entryPoints: ["src/main.ts"], + bundle: true, + external: [ + "obsidian", + "electron", + "@codemirror/autocomplete", + "@codemirror/collab", + "@codemirror/commands", + "@codemirror/language", + "@codemirror/lint", + "@codemirror/search", + "@codemirror/state", + "@codemirror/view", + "@lezer/common", + "@lezer/highlight", + "@lezer/lr", + ...builtins + ], + format: "cjs", + target: "es2018", + logLevel: "info", + sourcemap: production ? false : "inline", + treeShaking: true, + outfile: "main.js", + minify: production +}); + +if (production) { + await context.rebuild(); + await context.dispose(); +} else { + await context.watch(); +} diff --git a/main.js b/main.js new file mode 100644 index 0000000..3da0b1f --- /dev/null +++ b/main.js @@ -0,0 +1,296 @@ +const { Plugin, PluginSettingTab, Setting } = require("obsidian"); + +const DEFAULT_SETTINGS = { + maxWidth: 1600, + viewportMargin: 64, + widenTables: true, + widenCodeBlocks: true, + widenDiagrams: true, + widenImages: true, + noWrapTableCells: true, + readingView: true, + livePreview: true +}; + +module.exports = class DynamicWideContentPlugin extends Plugin { + async onload() { + await this.loadSettings(); + this.styleEl = document.createElement("style"); + this.styleEl.id = "dynamic-wide-content-styles"; + document.head.appendChild(this.styleEl); + this.register(() => this.styleEl?.remove()); + this.updateStyles(); + this.addSettingTab(new DynamicWideContentSettingTab(this.app, this)); + this.addCommand({ + id: "refresh-dynamic-wide-content", + name: "Refresh wide content styles", + callback: () => this.updateStyles() + }); + } + + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + + async saveSettings() { + await this.saveData(this.settings); + this.updateStyles(); + } + + updateStyles() { + if (!this.styleEl) return; + this.styleEl.textContent = buildCss(this.settings); + } +}; + +class DynamicWideContentSettingTab extends PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + + display() { + const { containerEl } = this; + containerEl.empty(); + containerEl.createEl("h2", { text: "Dynamic Wide Content" }); + containerEl.createEl("p", { + text: "Use wide tables, diagrams, images, and code blocks without widening normal prose." + }); + + new Setting(containerEl) + .setName("Maximum wide content width") + .setDesc("Controls how far wide blocks may expand while prose keeps Obsidian's readable width.") + .addSlider((slider) => slider + .setLimits(900, 2400, 50) + .setValue(this.plugin.settings.maxWidth) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.maxWidth = value; + await this.plugin.saveSettings(); + })); + + new Setting(containerEl) + .setName("Viewport side margin") + .setDesc("Keeps widened blocks away from the edge of the Obsidian pane.") + .addSlider((slider) => slider + .setLimits(0, 160, 8) + .setValue(this.plugin.settings.viewportMargin) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.viewportMargin = value; + await this.plugin.saveSettings(); + })); + + addToggle(containerEl, "Widen tables", "Allow Markdown tables to break out of the prose column.", this.plugin, "widenTables"); + addToggle(containerEl, "Widen diagrams", "Allow Mermaid and PlantUML diagrams to use a wider scrollable frame.", this.plugin, "widenDiagrams"); + addToggle(containerEl, "Widen images", "Allow embedded images to display wider than prose.", this.plugin, "widenImages"); + addToggle(containerEl, "Widen code blocks", "Allow preformatted code blocks to use a wider scrollable frame.", this.plugin, "widenCodeBlocks"); + addToggle(containerEl, "Keep table cells on one line", "Prevents wide tables from wrapping every cell into tall unreadable rows.", this.plugin, "noWrapTableCells"); + addToggle(containerEl, "Reading view support", "Apply selective widening in Reading view.", this.plugin, "readingView"); + addToggle(containerEl, "Live Preview support", "Apply selective widening to rendered embeds in Live Preview.", this.plugin, "livePreview"); + } +} + +function addToggle(containerEl, name, desc, plugin, key) { + new Setting(containerEl) + .setName(name) + .setDesc(desc) + .addToggle((toggle) => toggle + .setValue(plugin.settings[key]) + .onChange(async (value) => { + plugin.settings[key] = value; + await plugin.saveSettings(); + })); +} + +function buildCss(settings) { + const readingBlocks = []; + const innerBlocks = []; + const livePreviewBlocks = []; + const mediaContent = []; + + if (settings.widenTables) { + readingBlocks.push( + ".markdown-rendered .el-table", + ".markdown-rendered .markdown-preview-sizer > div:has(> table)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .table-wrapper)" + ); + innerBlocks.push(".markdown-rendered .table-wrapper"); + livePreviewBlocks.push( + ".markdown-source-view.mod-cm6 .cm-embed-block:has(table)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.table-wrapper)" + ); + } + + if (settings.widenCodeBlocks) { + readingBlocks.push( + ".markdown-rendered .el-pre", + ".markdown-rendered .markdown-preview-sizer > div:has(> pre)" + ); + innerBlocks.push(".markdown-rendered pre"); + livePreviewBlocks.push(".markdown-source-view.mod-cm6 .cm-embed-block:has(pre)"); + } + + if (settings.widenDiagrams) { + readingBlocks.push( + ".markdown-rendered .el-lang-mermaid", + ".markdown-rendered .el-lang-plantuml", + ".markdown-rendered .el-lang-plantuml-png", + ".markdown-rendered .el-lang-plantuml-svg", + ".markdown-rendered .el-lang-puml", + ".markdown-rendered .el-lang-puml-png", + ".markdown-rendered .el-lang-puml-svg", + ".markdown-rendered .markdown-preview-sizer > div:has(> .mermaid)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-mermaid)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-plantuml)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-plantuml-png)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-plantuml-svg)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-puml)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-puml-png)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-puml-svg)" + ); + innerBlocks.push( + ".markdown-rendered .mermaid", + ".markdown-rendered .block-language-mermaid", + ".markdown-rendered .block-language-plantuml", + ".markdown-rendered .block-language-plantuml-png", + ".markdown-rendered .block-language-plantuml-svg", + ".markdown-rendered .block-language-puml", + ".markdown-rendered .block-language-puml-png", + ".markdown-rendered .block-language-puml-svg", + ".markdown-rendered .plantuml-preview-view" + ); + livePreviewBlocks.push( + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.mermaid)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.block-language-mermaid)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.block-language-plantuml)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.block-language-puml)" + ); + mediaContent.push( + ".markdown-rendered .mermaid svg", + ".markdown-rendered .block-language-mermaid svg", + ".markdown-rendered .block-language-plantuml img", + ".markdown-rendered .block-language-plantuml svg", + ".markdown-rendered .block-language-plantuml-png img", + ".markdown-rendered .block-language-plantuml-svg svg", + ".markdown-rendered .block-language-puml img", + ".markdown-rendered .block-language-puml svg", + ".markdown-rendered .block-language-puml-png img", + ".markdown-rendered .block-language-puml-svg svg", + ".markdown-rendered .plantuml-preview-view img", + ".markdown-rendered .plantuml-preview-view svg" + ); + } + + if (settings.widenImages) { + readingBlocks.push( + ".markdown-rendered .el-embed-image", + ".markdown-rendered .markdown-preview-sizer > div:has(> .internal-embed.image-embed)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .image-embed)", + ".markdown-rendered .markdown-preview-sizer > div:has(img)" + ); + innerBlocks.push( + ".markdown-rendered .internal-embed.image-embed", + ".markdown-rendered .image-embed" + ); + livePreviewBlocks.push( + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.internal-embed.image-embed)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.image-embed)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(img)" + ); + mediaContent.push( + ".markdown-rendered .internal-embed.image-embed img", + ".markdown-rendered .image-embed img" + ); + } + + const css = [ + `body { + --dynamic-wide-content-max: min(${settings.maxWidth}px, calc(100vw - ${settings.viewportMargin}px)); +}` + ]; + + if (settings.readingView) { + css.push(` +.markdown-preview-view.is-readable-line-width .markdown-preview-sizer, +.markdown-reading-view.is-readable-line-width .markdown-preview-sizer, +.markdown-preview-view .markdown-preview-section, +.markdown-reading-view .markdown-preview-section { + overflow: visible !important; +}`); + + if (readingBlocks.length > 0) { + css.push(` +${readingBlocks.join(",\n")} { + position: relative !important; + left: 50% !important; + width: max-content !important; + max-width: var(--dynamic-wide-content-max) !important; + margin-top: 0.75em !important; + margin-bottom: 0.75em !important; + overflow-x: auto !important; + transform: translateX(-50%) !important; +}`); + } + + if (innerBlocks.length > 0) { + css.push(` +${innerBlocks.join(",\n")} { + max-width: 100% !important; + overflow-x: auto !important; +}`); + } + } + + if (settings.livePreview && livePreviewBlocks.length > 0) { + css.push(` +.markdown-source-view.mod-cm6.is-readable-line-width .cm-contentContainer { + overflow: visible !important; +} + +${livePreviewBlocks.join(",\n")} { + position: relative !important; + left: 50% !important; + width: max-content !important; + max-width: var(--dynamic-wide-content-max) !important; + overflow-x: auto !important; + transform: translateX(-50%) !important; +}`); + } + + if (settings.widenTables) { + css.push(` +.markdown-rendered table { + display: table !important; + width: max-content !important; + min-width: 100% !important; + max-width: none !important; +}`); + } + + if (settings.widenTables && settings.noWrapTableCells) { + css.push(` +.markdown-rendered th, +.markdown-rendered td { + white-space: nowrap !important; +}`); + } + + if (settings.widenCodeBlocks) { + css.push(` +.markdown-rendered pre > code { + white-space: pre !important; +}`); + } + + if (mediaContent.length > 0) { + css.push(` +${mediaContent.join(",\n")} { + width: auto !important; + max-width: none !important; + height: auto !important; +}`); + } + + return css.join("\n"); +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..6dced44 --- /dev/null +++ b/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "dynamic-wide-content", + "name": "Dynamic Wide Content", + "version": "1.0.0", + "minAppVersion": "1.5.0", + "description": "Display wide tables, diagrams, images, and code blocks in scrollable wide frames while preserving Obsidian's normal readable prose width.", + "author": "Panagiotis Koletsos", + "authorUrl": "https://github.com/pcoletsos", + "isDesktopOnly": false +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f6bd375 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "obsidian-dynamic-wide-content", + "version": "1.0.0", + "description": "Wide tables, diagrams, images, and code blocks without widening your Obsidian prose.", + "main": "main.js", + "scripts": { + "dev": "node esbuild.config.mjs", + "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production" + }, + "keywords": [ + "obsidian", + "obsidian-plugin", + "markdown", + "tables", + "mermaid", + "plantuml", + "wide-content", + "readable-line-length" + ], + "author": "Panagiotis Koletsos", + "license": "MIT", + "devDependencies": { + "@types/node": "^20.11.30", + "builtin-modules": "^3.3.0", + "esbuild": "^0.20.2", + "obsidian": "latest", + "typescript": "^5.4.3" + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..6b558b1 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,329 @@ +import { App, Plugin, PluginSettingTab, Setting } from "obsidian"; + +interface DynamicWideContentSettings { + maxWidth: number; + viewportMargin: number; + widenTables: boolean; + widenCodeBlocks: boolean; + widenDiagrams: boolean; + widenImages: boolean; + noWrapTableCells: boolean; + readingView: boolean; + livePreview: boolean; +} + +const DEFAULT_SETTINGS: DynamicWideContentSettings = { + maxWidth: 1600, + viewportMargin: 64, + widenTables: true, + widenCodeBlocks: true, + widenDiagrams: true, + widenImages: true, + noWrapTableCells: true, + readingView: true, + livePreview: true +}; + +export default class DynamicWideContentPlugin extends Plugin { + settings: DynamicWideContentSettings; + private styleEl: HTMLStyleElement | null = null; + + async onload() { + await this.loadSettings(); + + this.styleEl = document.createElement("style"); + this.styleEl.id = "dynamic-wide-content-styles"; + document.head.appendChild(this.styleEl); + this.register(() => this.styleEl?.remove()); + + this.updateStyles(); + + this.addSettingTab(new DynamicWideContentSettingTab(this.app, this)); + this.addCommand({ + id: "refresh-dynamic-wide-content", + name: "Refresh wide content styles", + callback: () => this.updateStyles() + }); + } + + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + + async saveSettings() { + await this.saveData(this.settings); + this.updateStyles(); + } + + updateStyles() { + if (!this.styleEl) return; + this.styleEl.textContent = buildCss(this.settings); + } +} + +class DynamicWideContentSettingTab extends PluginSettingTab { + plugin: DynamicWideContentPlugin; + + constructor(app: App, plugin: DynamicWideContentPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + containerEl.empty(); + containerEl.createEl("h2", { text: "Dynamic Wide Content" }); + containerEl.createEl("p", { + text: "Use wide tables, diagrams, images, and code blocks without widening normal prose." + }); + + new Setting(containerEl) + .setName("Maximum wide content width") + .setDesc("Controls how far wide blocks may expand while prose keeps Obsidian's readable width.") + .addSlider((slider) => slider + .setLimits(900, 2400, 50) + .setValue(this.plugin.settings.maxWidth) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.maxWidth = value; + await this.plugin.saveSettings(); + })); + + new Setting(containerEl) + .setName("Viewport side margin") + .setDesc("Keeps widened blocks away from the edge of the Obsidian pane.") + .addSlider((slider) => slider + .setLimits(0, 160, 8) + .setValue(this.plugin.settings.viewportMargin) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.viewportMargin = value; + await this.plugin.saveSettings(); + })); + + addToggle(containerEl, "Widen tables", "Allow Markdown tables to break out of the prose column.", this.plugin, "widenTables"); + addToggle(containerEl, "Widen diagrams", "Allow Mermaid and PlantUML diagrams to use a wider scrollable frame.", this.plugin, "widenDiagrams"); + addToggle(containerEl, "Widen images", "Allow embedded images to display wider than prose.", this.plugin, "widenImages"); + addToggle(containerEl, "Widen code blocks", "Allow preformatted code blocks to use a wider scrollable frame.", this.plugin, "widenCodeBlocks"); + addToggle(containerEl, "Keep table cells on one line", "Prevents wide tables from wrapping every cell into tall unreadable rows.", this.plugin, "noWrapTableCells"); + addToggle(containerEl, "Reading view support", "Apply selective widening in Reading view.", this.plugin, "readingView"); + addToggle(containerEl, "Live Preview support", "Apply selective widening to rendered embeds in Live Preview.", this.plugin, "livePreview"); + } +} + +function addToggle( + containerEl: HTMLElement, + name: string, + desc: string, + plugin: DynamicWideContentPlugin, + key: keyof Pick +) { + new Setting(containerEl) + .setName(name) + .setDesc(desc) + .addToggle((toggle) => toggle + .setValue(plugin.settings[key]) + .onChange(async (value) => { + plugin.settings[key] = value; + await plugin.saveSettings(); + })); +} + +function buildCss(settings: DynamicWideContentSettings): string { + const readingBlocks: string[] = []; + const innerBlocks: string[] = []; + const livePreviewBlocks: string[] = []; + const mediaContent: string[] = []; + + if (settings.widenTables) { + readingBlocks.push( + ".markdown-rendered .el-table", + ".markdown-rendered .markdown-preview-sizer > div:has(> table)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .table-wrapper)" + ); + innerBlocks.push(".markdown-rendered .table-wrapper"); + livePreviewBlocks.push( + ".markdown-source-view.mod-cm6 .cm-embed-block:has(table)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.table-wrapper)" + ); + } + + if (settings.widenCodeBlocks) { + readingBlocks.push( + ".markdown-rendered .el-pre", + ".markdown-rendered .markdown-preview-sizer > div:has(> pre)" + ); + innerBlocks.push(".markdown-rendered pre"); + livePreviewBlocks.push(".markdown-source-view.mod-cm6 .cm-embed-block:has(pre)"); + } + + if (settings.widenDiagrams) { + readingBlocks.push( + ".markdown-rendered .el-lang-mermaid", + ".markdown-rendered .el-lang-plantuml", + ".markdown-rendered .el-lang-plantuml-png", + ".markdown-rendered .el-lang-plantuml-svg", + ".markdown-rendered .el-lang-puml", + ".markdown-rendered .el-lang-puml-png", + ".markdown-rendered .el-lang-puml-svg", + ".markdown-rendered .markdown-preview-sizer > div:has(> .mermaid)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-mermaid)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-plantuml)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-plantuml-png)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-plantuml-svg)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-puml)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-puml-png)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .block-language-puml-svg)" + ); + innerBlocks.push( + ".markdown-rendered .mermaid", + ".markdown-rendered .block-language-mermaid", + ".markdown-rendered .block-language-plantuml", + ".markdown-rendered .block-language-plantuml-png", + ".markdown-rendered .block-language-plantuml-svg", + ".markdown-rendered .block-language-puml", + ".markdown-rendered .block-language-puml-png", + ".markdown-rendered .block-language-puml-svg", + ".markdown-rendered .plantuml-preview-view" + ); + livePreviewBlocks.push( + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.mermaid)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.block-language-mermaid)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.block-language-plantuml)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.block-language-puml)" + ); + mediaContent.push( + ".markdown-rendered .mermaid svg", + ".markdown-rendered .block-language-mermaid svg", + ".markdown-rendered .block-language-plantuml img", + ".markdown-rendered .block-language-plantuml svg", + ".markdown-rendered .block-language-plantuml-png img", + ".markdown-rendered .block-language-plantuml-svg svg", + ".markdown-rendered .block-language-puml img", + ".markdown-rendered .block-language-puml svg", + ".markdown-rendered .block-language-puml-png img", + ".markdown-rendered .block-language-puml-svg svg", + ".markdown-rendered .plantuml-preview-view img", + ".markdown-rendered .plantuml-preview-view svg" + ); + } + + if (settings.widenImages) { + readingBlocks.push( + ".markdown-rendered .el-embed-image", + ".markdown-rendered .markdown-preview-sizer > div:has(> .internal-embed.image-embed)", + ".markdown-rendered .markdown-preview-sizer > div:has(> .image-embed)", + ".markdown-rendered .markdown-preview-sizer > div:has(img)" + ); + innerBlocks.push( + ".markdown-rendered .internal-embed.image-embed", + ".markdown-rendered .image-embed" + ); + livePreviewBlocks.push( + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.internal-embed.image-embed)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(.image-embed)", + ".markdown-source-view.mod-cm6 .cm-embed-block:has(img)" + ); + mediaContent.push( + ".markdown-rendered .internal-embed.image-embed img", + ".markdown-rendered .image-embed img" + ); + } + + const css: string[] = [ + `body { + --dynamic-wide-content-max: min(${settings.maxWidth}px, calc(100vw - ${settings.viewportMargin}px)); +}` + ]; + + if (settings.readingView) { + css.push(` +.markdown-preview-view.is-readable-line-width .markdown-preview-sizer, +.markdown-reading-view.is-readable-line-width .markdown-preview-sizer, +.markdown-preview-view .markdown-preview-section, +.markdown-reading-view .markdown-preview-section { + overflow: visible !important; +}`); + + if (readingBlocks.length > 0) { + css.push(` +${readingBlocks.join(",\n")} { + position: relative !important; + left: 50% !important; + width: max-content !important; + max-width: var(--dynamic-wide-content-max) !important; + margin-top: 0.75em !important; + margin-bottom: 0.75em !important; + overflow-x: auto !important; + transform: translateX(-50%) !important; +}`); + } + + if (innerBlocks.length > 0) { + css.push(` +${innerBlocks.join(",\n")} { + max-width: 100% !important; + overflow-x: auto !important; +}`); + } + } + + if (settings.livePreview && livePreviewBlocks.length > 0) { + css.push(` +.markdown-source-view.mod-cm6.is-readable-line-width .cm-contentContainer { + overflow: visible !important; +} + +${livePreviewBlocks.join(",\n")} { + position: relative !important; + left: 50% !important; + width: max-content !important; + max-width: var(--dynamic-wide-content-max) !important; + overflow-x: auto !important; + transform: translateX(-50%) !important; +}`); + } + + if (settings.widenTables) { + css.push(` +.markdown-rendered table { + display: table !important; + width: max-content !important; + min-width: 100% !important; + max-width: none !important; +}`); + } + + if (settings.widenTables && settings.noWrapTableCells) { + css.push(` +.markdown-rendered th, +.markdown-rendered td { + white-space: nowrap !important; +}`); + } + + if (settings.widenCodeBlocks) { + css.push(` +.markdown-rendered pre > code { + white-space: pre !important; +}`); + } + + if (mediaContent.length > 0) { + css.push(` +${mediaContent.join(",\n")} { + width: auto !important; + max-width: none !important; + height: auto !important; +}`); + } + + return css.join("\n"); +} diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..bd4362f --- /dev/null +++ b/styles.css @@ -0,0 +1,5 @@ +/* +Dynamic Wide Content injects its active CSS from main.js so user settings can +change the selector set and maximum width. This file is intentionally minimal +but kept for Obsidian community-plugin release packaging. +*/ diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ce4a7e2 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "ES2018", + "allowJs": true, + "noImplicitAny": true, + "moduleResolution": "node", + "importHelpers": true, + "isolatedModules": true, + "strictNullChecks": true, + "lib": [ + "DOM", + "ES2018" + ] + }, + "include": [ + "**/*.ts" + ] +} diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..27ec1ef --- /dev/null +++ b/versions.json @@ -0,0 +1,3 @@ +{ + "1.0.0": "1.5.0" +}