diff --git a/apps/client/src/print.css b/apps/client/src/print.css index aa9f49a1a2..d0123c810e 100644 --- a/apps/client/src/print.css +++ b/apps/client/src/print.css @@ -5,6 +5,10 @@ --ck-content-color-image-caption-background: transparent !important; } +@page { + margin: 2cm; +} + html, body { width: 100%; diff --git a/apps/client/src/widgets/layout/StatusBar.tsx b/apps/client/src/widgets/layout/StatusBar.tsx index 2be8a8d40a..077c59ef4f 100644 --- a/apps/client/src/widgets/layout/StatusBar.tsx +++ b/apps/client/src/widgets/layout/StatusBar.tsx @@ -446,7 +446,7 @@ function TabWidthSwitcher({ note, noteContext }: StatusBarContext) { const [ globalUseTabs ] = useTriliumOptionBool("codeNoteIndentWithTabs"); const [ noteTabWidth, setNoteTabWidth ] = useNoteLabelInt(note, "tabWidth"); const [ noteUseTabs, setNoteUseTabs ] = useNoteLabelOptionalBool(note, "indentWithTabs"); - const effectiveTabWidth = noteTabWidth ?? globalTabWidth; + const effectiveTabWidth = noteTabWidth ?? globalTabWidth ?? 4; const effectiveUseTabs = noteUseTabs ?? globalUseTabs; const hasWidthOverride = noteTabWidth != null; const hasStyleOverride = noteUseTabs != null; diff --git a/apps/client/src/widgets/layout/reindentation.ts b/apps/client/src/widgets/layout/reindentation.ts index d08234ff05..39276086d7 100644 --- a/apps/client/src/widgets/layout/reindentation.ts +++ b/apps/client/src/widgets/layout/reindentation.ts @@ -29,7 +29,7 @@ function measureLeadingColumns(leading: string, tabWidth: number): number { */ export function convertIndentation(content: string, from: IndentStyle, to: IndentStyle): string { if (from.useTabs === to.useTabs && from.width === to.width) return content; - if (from.width <= 0 || to.width <= 0) return content; + if (!Number.isFinite(from.width) || !Number.isFinite(to.width) || from.width <= 0 || to.width <= 0) return content; const toUnit = to.useTabs ? "\t" : " ".repeat(to.width); return content.replace(/^[ \t]+/gm, (leading) => { diff --git a/apps/client/src/widgets/react/FormTextBox.tsx b/apps/client/src/widgets/react/FormTextBox.tsx index 27df6326cb..c8672c515a 100644 --- a/apps/client/src/widgets/react/FormTextBox.tsx +++ b/apps/client/src/widgets/react/FormTextBox.tsx @@ -19,6 +19,9 @@ export default function FormTextBox({ inputRef, className, type, currentValue, o if (type === "number") { const { min, max } = rest; const currentValueNum = parseInt(value, 10); + if (!Number.isFinite(currentValueNum)) { + return String(min ?? ""); + } if (min && currentValueNum < parseInt(String(min), 10)) { return String(min); } else if (max && currentValueNum > parseInt(String(max), 10)) { diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 66a3e44dfa..5b23276923 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -682,8 +682,9 @@ export function useNoteLabelInt(note: FNote | undefined | null, labelName: Filte //@ts-expect-error `useNoteLabel` only accepts string properties but we need to be able to read number ones. const [ value, setValue ] = useNoteLabel(note, labelName); useDebugValue(labelName); + const parsed = value ? parseInt(value, 10) : undefined; return [ - (value ? parseInt(value, 10) : undefined), + (Number.isFinite(parsed) ? parsed : undefined), (newValue) => setValue(newValue === null ? null : String(newValue)) ]; } diff --git a/apps/client/src/widgets/type_widgets/code/CodeMirror.tsx b/apps/client/src/widgets/type_widgets/code/CodeMirror.tsx index 609ffe0198..c52c8e7676 100644 --- a/apps/client/src/widgets/type_widgets/code/CodeMirror.tsx +++ b/apps/client/src/widgets/type_widgets/code/CodeMirror.tsx @@ -51,10 +51,8 @@ export default function CodeMirror({ className, content, mime, editorRef: extern // React to indent size / style changes. useEffect(() => { - if (extraOpts.indentSize != null) { - codeEditorRef.current?.setIndent(extraOpts.indentSize, !!extraOpts.useTabs); - } else if (extraOpts.useTabs != null) { - codeEditorRef.current?.setUseTabs(extraOpts.useTabs); + if (extraOpts.indentSize != null || extraOpts.useTabs != null) { + codeEditorRef.current?.setIndent(extraOpts.indentSize ?? 4, !!extraOpts.useTabs); } }, [ extraOpts.indentSize, extraOpts.useTabs ]); diff --git a/apps/client/src/widgets/type_widgets/options/text_notes.tsx b/apps/client/src/widgets/type_widgets/options/text_notes.tsx index 7a6319562e..2d324de950 100644 --- a/apps/client/src/widgets/type_widgets/options/text_notes.tsx +++ b/apps/client/src/widgets/type_widgets/options/text_notes.tsx @@ -341,7 +341,7 @@ function CodeBlockPreview({ theme, wordWrap, tabWidth }: { theme: string, wordWr const codeStyle: CSSProperties = useMemo(() => { return { whiteSpace: wordWrap ? "pre-wrap" : "pre", - tabSize: tabWidth ?? "4" + tabSize: tabWidth || "4" }; }, [ wordWrap, tabWidth ]); diff --git a/apps/client/src/widgets/type_widgets/text/EditableText.tsx b/apps/client/src/widgets/type_widgets/text/EditableText.tsx index 2caf37c661..8cacc40e25 100644 --- a/apps/client/src/widgets/type_widgets/text/EditableText.tsx +++ b/apps/client/src/widgets/type_widgets/text/EditableText.tsx @@ -221,7 +221,7 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext const onWatchdogStateChange = useWatchdogCrashHandling(); useEffect(() => { - document.body.style.setProperty("--code-block-tab-width", codeBlockTabWidth ?? "4"); + document.body.style.setProperty("--code-block-tab-width", codeBlockTabWidth || "4"); }, [codeBlockTabWidth]); return ( diff --git a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx index b99e81b19a..172abb6990 100644 --- a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx +++ b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx @@ -28,7 +28,7 @@ export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetPro const { isRtl } = useNoteLanguage(note); useEffect(() => { - document.body.style.setProperty("--code-block-tab-width", codeBlockTabWidth ?? "4"); + document.body.style.setProperty("--code-block-tab-width", codeBlockTabWidth || "4"); }, [codeBlockTabWidth]); // Apply necessary transforms. diff --git a/apps/server/src/services/printing.ts b/apps/server/src/services/printing.ts index 30e415999f..3193f4743b 100644 --- a/apps/server/src/services/printing.ts +++ b/apps/server/src/services/printing.ts @@ -221,6 +221,7 @@ export function initPrintingHandlers() { scale, margins: parseMargins(margins), pageRanges: pageRanges || undefined, + preferCSSPageSize: false, generateDocumentOutline: true, generateTaggedPDF: true, printBackground: true, @@ -272,6 +273,7 @@ export function initPrintingHandlers() { scale, margins: parseMargins(margins), pageRanges: pageRanges || undefined, + preferCSSPageSize: false, generateDocumentOutline: true, generateTaggedPDF: true, printBackground: true, @@ -350,6 +352,8 @@ export function initPrintingHandlers() { // print() accepts most of the same options as printToPDF, but typing differs // slightly (e.g. no "Ledger" pageSize). Cast to keep this concise. + // "Ledger" and "Tabloid" are the same physical size (11×17 in); Electron's + // print() API only recognises "Tabloid", so we map "Ledger" to "Tabloid". const printOpts: Electron.WebContentsPrintOptions = { silent, deviceName, diff --git a/packages/codemirror/src/index.ts b/packages/codemirror/src/index.ts index 516ab8e686..0fa8023ddb 100644 --- a/packages/codemirror/src/index.ts +++ b/packages/codemirror/src/index.ts @@ -183,6 +183,8 @@ export default class CodeMirror extends EditorView { } setIndent(size: number, useTabs: boolean) { + if (!Number.isFinite(size) || size < 1) size = 4; + if (size > 16) size = 16; this.config.indentSize = size; this.config.useTabs = useTabs; this.dispatch({ diff --git a/packages/commons/src/lib/builtin_attributes.ts b/packages/commons/src/lib/builtin_attributes.ts index 0c23632a6c..76cdf033dc 100644 --- a/packages/commons/src/lib/builtin_attributes.ts +++ b/packages/commons/src/lib/builtin_attributes.ts @@ -83,6 +83,10 @@ export default [ { type: "label", name: "iconPack", isDangerous: true }, { type: "label", name: "docName", isDangerous: true }, + { type: "label", name: "tabWidth" }, + { type: "label", name: "indentWithTabs" }, + { type: "label", name: "wrapLines" }, + { type: "label", name: "printLandscape" }, { type: "label", name: "printPageSize" }, { type: "label", name: "printScale" },