feat: scaffold dynamic wide content plugin

This commit is contained in:
Panagiotis Koletsos 2026-06-13 17:39:16 +03:00
commit 3c2c0f7ba6
11 changed files with 854 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
node_modules/
.DS_Store
*.log
.obsidian/
data.json

21
LICENSE Normal file
View file

@ -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.

91
README.md Normal file
View file

@ -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

43
esbuild.config.mjs Normal file
View file

@ -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();
}

296
main.js Normal file
View file

@ -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");
}

10
manifest.json Normal file
View file

@ -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
}

29
package.json Normal file
View file

@ -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"
}
}

329
src/main.ts Normal file
View file

@ -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<DynamicWideContentSettings,
"widenTables" |
"widenCodeBlocks" |
"widenDiagrams" |
"widenImages" |
"noWrapTableCells" |
"readingView" |
"livePreview">
) {
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");
}

5
styles.css Normal file
View file

@ -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.
*/

22
tsconfig.json Normal file
View file

@ -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"
]
}

3
versions.json Normal file
View file

@ -0,0 +1,3 @@
{
"1.0.0": "1.5.0"
}