Files
ttrpg-tools/src/components/md-print/config.ts
T
hypercross 4a19336445 feat(md-print): add A4/A5 multi-column print layout
Add an md-print element that paginates markdown into A4 landscape sheets holding two A5 pages, each with configurable columns. The preview modal exports through the browser print dialog.

Paragraphs, lists, and tables split at word, item, and row boundaries; headings are kept with following content.
2026-10-23 11:00:59 +08:00

53 lines
1.7 KiB
TypeScript

import type { PrintConfig } from "./types";
export const PRINT_DEFAULTS: PrintConfig = {
columns: 2,
margin: 10,
gap: 5,
font: 9,
title: "",
pageNumbers: true,
cutMarks: true,
};
function toNumber(value: unknown, fallback: number): number {
const n = typeof value === "number" ? value : Number(value);
return Number.isFinite(n) ? n : fallback;
}
function toBoolean(value: unknown, fallback: boolean): boolean {
if (typeof value === "boolean") return value;
if (typeof value === "string") {
const s = value.trim().toLowerCase();
if (["true", "1", "yes", "on"].includes(s)) return true;
if (["false", "0", "no", "off"].includes(s)) return false;
}
return fallback;
}
function clamp(value: number, min: number, max: number): number {
return Math.min(max, Math.max(min, value));
}
/**
* Normalize directive attributes / YAML `data-config` into a usable config.
* Values may arrive as strings (directive attributes) or numbers/booleans
* (YAML `data-config`), so everything is coerced and clamped.
*/
export function normalizePrintConfig(
input: Partial<Record<keyof PrintConfig, unknown>> = {},
): PrintConfig {
return {
columns: Math.round(
clamp(toNumber(input.columns, PRINT_DEFAULTS.columns), 1, 4),
),
margin: clamp(toNumber(input.margin, PRINT_DEFAULTS.margin), 5, 40),
gap: clamp(toNumber(input.gap, PRINT_DEFAULTS.gap), 0, 20),
font: clamp(toNumber(input.font, PRINT_DEFAULTS.font), 6, 16),
title:
typeof input.title === "string" ? input.title : PRINT_DEFAULTS.title,
pageNumbers: toBoolean(input.pageNumbers, PRINT_DEFAULTS.pageNumbers),
cutMarks: toBoolean(input.cutMarks, PRINT_DEFAULTS.cutMarks),
};
}