Skip to content

Commit f5d1fb5

Browse files
committed
feat(mdviewer): add print button with light theme and proper page layout
- Add printer icon button to both reader and edit mode toolbars - Force light theme CSS variables in @media print for readable output - Hide toolbar and all UI chrome during print via CSS - Override height/overflow constraints so full content prints - Add allow-modals to mdviewer sandbox for window.print() - Standard page margins (20mm top/bottom, 25mm left/right)
1 parent 912035a commit f5d1fb5

4 files changed

Lines changed: 81 additions & 3 deletions

File tree

src-mdviewer/src/components/embedded-toolbar.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import {
2929
MoreHorizontal,
3030
BookOpen,
3131
Link2,
32-
Link2Off
32+
Link2Off,
33+
Printer
3334
} from "lucide";
3435
import { on, emit } from "../core/events.js";
3536
import { getState, setState } from "../core/state.js";
@@ -46,7 +47,7 @@ const THRESHOLD_LISTS = 520; // then lists
4647
const THRESHOLD_TEXT = 420; // finally text formatting
4748

4849
const allIcons = { Bold, Italic, Strikethrough, Underline, Code, Link, List, ListOrdered,
49-
ListChecks, Quote, Minus, Table, FileCode, ChevronDown, Type, MoreHorizontal, Pencil, BookOpen, Link2, Link2Off };
50+
ListChecks, Quote, Minus, Table, FileCode, ChevronDown, Type, MoreHorizontal, Pencil, BookOpen, Link2, Link2Off, Printer };
5051

5152
export function initEmbeddedToolbar() {
5253
toolbar = document.getElementById("toolbar");
@@ -79,6 +80,9 @@ function render() {
7980
function renderReadMode() {
8081
toolbar.innerHTML = `<div class="embedded-toolbar">
8182
<div class="toolbar-spacer"></div>
83+
<button class="toolbar-btn print-btn" id="emb-print-btn" data-tooltip="${t("toolbar.print") || "Print"}">
84+
<i data-lucide="printer"></i>
85+
</button>
8286
<button class="toolbar-btn cursor-sync-btn${cursorSyncEnabled ? " active" : ""}" id="emb-cursor-sync" data-tooltip="${t("toolbar.cursor_sync") || "Cursor sync"}" aria-pressed="${cursorSyncEnabled}">
8387
<i data-lucide="link-2" class="sync-on-icon"${cursorSyncEnabled ? "" : ' style="display:none"'}></i>
8488
<i data-lucide="link-2-off" class="sync-off-icon"${cursorSyncEnabled ? ' style="display:none"' : ""}></i>
@@ -94,6 +98,7 @@ function renderReadMode() {
9498
toolbar.querySelectorAll("svg[data-lucide]").forEach(svg => svg.removeAttribute("data-lucide"));
9599

96100
wireCursorSyncButton();
101+
wirePrintButton();
97102

98103
const editBtn = document.getElementById("emb-edit-btn");
99104
if (editBtn) {
@@ -179,6 +184,9 @@ function renderEditMode(level) {
179184
toolbar.innerHTML = `<div class="embedded-toolbar">
180185
${formatRow}
181186
<div class="toolbar-spacer"></div>
187+
<button class="toolbar-btn print-btn" id="emb-print-btn" data-tooltip="${t("toolbar.print") || "Print"}">
188+
<i data-lucide="printer"></i>
189+
</button>
182190
<button class="toolbar-btn cursor-sync-btn${cursorSyncEnabled ? " active" : ""}" id="emb-cursor-sync" data-tooltip="${t("toolbar.cursor_sync") || "Cursor sync"}" aria-pressed="${cursorSyncEnabled}">
183191
<i data-lucide="link-2" class="sync-on-icon"${cursorSyncEnabled ? "" : ' style="display:none"'}></i>
184192
<i data-lucide="link-2-off" class="sync-off-icon"${cursorSyncEnabled ? ' style="display:none"' : ""}></i>
@@ -199,6 +207,7 @@ function renderEditMode(level) {
199207
wireDropdowns();
200208
}
201209
wireCursorSyncButton();
210+
wirePrintButton();
202211
wireDoneButton();
203212
}
204213

@@ -287,6 +296,15 @@ function wireCursorSyncButton() {
287296
}
288297
}
289298

299+
function wirePrintButton() {
300+
const printBtn = document.getElementById("emb-print-btn");
301+
if (printBtn) {
302+
printBtn.addEventListener("click", () => {
303+
window.print();
304+
});
305+
}
306+
}
307+
290308
function wireDoneButton() {
291309
const doneBtn = document.getElementById("emb-done-btn");
292310
if (doneBtn) {

src-mdviewer/src/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"switch_to_reader": "Switch to reader mode",
2020
"switch_to_edit": "Switch to edit mode",
2121
"cursor_sync": "Toggle cursor sync",
22+
"print": "Print",
2223
"empty_line_hint": "Type / to insert"
2324
},
2425
"sidebar": {

src-mdviewer/src/styles/editor.css

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,41 @@
12431243

12441244
/* ===== Print ===== */
12451245
@media print {
1246+
@page {
1247+
margin: 20mm 25mm;
1248+
}
1249+
1250+
/* Force light theme colors for print */
1251+
:root, [data-theme] {
1252+
--color-bg: #ffffff !important;
1253+
--color-bg-secondary: #f6f8fa !important;
1254+
--color-bg-tertiary: #eaeef2 !important;
1255+
--color-surface: #ffffff !important;
1256+
--color-text: #1f2328 !important;
1257+
--color-text-secondary: #656d76 !important;
1258+
--color-text-tertiary: #8b949e !important;
1259+
--color-accent: #0969da !important;
1260+
--color-border: #d0d7de !important;
1261+
--color-border-subtle: #e8ecf0 !important;
1262+
--color-code-bg: #f6f8fa !important;
1263+
--color-code-text: #1f2328 !important;
1264+
--color-code-border: #d0d7de !important;
1265+
--color-link: #0969da !important;
1266+
--color-alert-note: #0969da !important;
1267+
--color-alert-note-bg: #ddf4ff !important;
1268+
--color-alert-tip: #1a7f37 !important;
1269+
--color-alert-tip-bg: #dafbe1 !important;
1270+
--color-alert-important: #8250df !important;
1271+
--color-alert-important-bg: #fbefff !important;
1272+
--color-alert-warning: #9a6700 !important;
1273+
--color-alert-warning-bg: #fff8c5 !important;
1274+
--color-alert-caution: #d1242f !important;
1275+
--color-alert-caution-bg: #ffebe9 !important;
1276+
--color-table-border: #d0d7de !important;
1277+
--color-table-row-alt: #f6f8fa !important;
1278+
}
1279+
1280+
#toolbar,
12461281
.raw-editor-panel,
12471282
.raw-editor-panel .cm-editor,
12481283
.toolbar-edit-group,
@@ -1266,4 +1301,28 @@
12661301
.toast {
12671302
display: none !important;
12681303
}
1304+
1305+
html, body {
1306+
height: auto !important;
1307+
overflow: visible !important;
1308+
}
1309+
1310+
#app {
1311+
display: block !important;
1312+
height: auto !important;
1313+
overflow: visible !important;
1314+
}
1315+
1316+
.app-viewer {
1317+
overflow: visible !important;
1318+
height: auto !important;
1319+
min-height: 0 !important;
1320+
}
1321+
1322+
#viewer-content {
1323+
padding: 0 !important;
1324+
margin: 0 !important;
1325+
overflow: visible !important;
1326+
height: auto !important;
1327+
}
12691328
}

src/extensionsIntegrated/Phoenix-live-preview/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ define(function (require, exports, module) {
140140
// no allow-forms, allow-modals, allow-pointer-lock (not needed for markdown editing).
141141
// Communication works via MarkdownSync's own message handler (bypasses EventManager origin check).
142142
const _mdSandboxAttr = Phoenix.isTestWindow ? "" :
143-
'sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox"';
143+
'sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox allow-modals"';
144144
const MDVIEWR_IFRAME_HTML = `
145145
<iframe id="${LIVE_PREVIEW_IFRAME_ID}" title="Live Preview" style="border: none"
146146
width="100%" height="100%" seamless="true"

0 commit comments

Comments
 (0)