@cenk1cenk2/md-printer 2.21.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +333 -48
  2. package/bin/dev.js +0 -2
  3. package/bin/run.js +0 -1
  4. package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs +1 -2
  5. package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.mjs +1 -2
  6. package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateParam.mjs +8 -0
  7. package/dist/commands/index.mjs +32 -345
  8. package/dist/components/Date.svelte +1 -0
  9. package/dist/components/Markdown.svelte +20 -0
  10. package/dist/components/Nodes.svelte +32 -0
  11. package/dist/components/Page.svelte +20 -0
  12. package/dist/components/PageBreak.svelte +5 -0
  13. package/dist/components/PageNumber.svelte +1 -0
  14. package/dist/components/SignatureBlock.svelte +56 -0
  15. package/dist/components/Title.svelte +1 -0
  16. package/dist/components/TotalPages.svelte +1 -0
  17. package/dist/components/index.ts +8 -0
  18. package/dist/constants/file.constants.mjs +1 -8
  19. package/dist/constants/index.mjs +5 -4
  20. package/dist/constants/preview.constants.mjs +9 -0
  21. package/dist/constants/signature.constants.mjs +7 -0
  22. package/dist/constants/template.constants.mjs +48 -10
  23. package/dist/interfaces/commands/index.mjs +1 -1
  24. package/dist/interfaces/commands/run.interface.mjs +1 -1
  25. package/dist/interfaces/index.mjs +1 -1
  26. package/dist/interfaces/markdown.interface.mjs +1 -0
  27. package/dist/interfaces/renderer.interface.mjs +1 -0
  28. package/dist/interfaces/session.interface.mjs +1 -0
  29. package/dist/interfaces/signature.interface.mjs +1 -1
  30. package/dist/interfaces/template.interface.mjs +1 -0
  31. package/dist/modules/md-printer/md-printer.handler.mjs +113 -0
  32. package/dist/modules/md-printer/md-printer.module.mjs +21 -0
  33. package/dist/modules/md-printer/md-printer.schema.mjs +50 -0
  34. package/dist/services/i18n.service.mjs +12 -15
  35. package/dist/services/index.mjs +9 -2
  36. package/dist/services/loopback.mjs +18 -0
  37. package/dist/services/markdown.service.mjs +123 -0
  38. package/dist/services/printer.module.mjs +39 -0
  39. package/dist/services/printer.service.mjs +171 -0
  40. package/dist/services/renderer/browser.mjs +41 -0
  41. package/dist/services/renderer/browser.service.mjs +56 -0
  42. package/dist/services/renderer/renderer.service.mjs +198 -0
  43. package/dist/services/session/Preview.svelte +238 -0
  44. package/dist/services/session/preview.service.mjs +187 -0
  45. package/dist/services/session/session.service.mjs +115 -0
  46. package/dist/services/signature/adapters/a-trust.adapter.mjs +32 -36
  47. package/dist/services/signature/index.mjs +1 -2
  48. package/dist/services/signature/signature.adapter.mjs +1 -2
  49. package/dist/services/signature/signature.service.mjs +6 -10
  50. package/dist/services/template/Document.svelte +21 -0
  51. package/dist/services/template/Margin.svelte +15 -0
  52. package/dist/services/template/assets.mjs +127 -0
  53. package/dist/services/template/render.entry.ts +32 -0
  54. package/dist/services/template/svelte.plugin.mjs +66 -0
  55. package/dist/services/template/template.service.mjs +211 -0
  56. package/oclif.manifest.json +49 -36
  57. package/package.json +69 -35
  58. package/templates/default/Template.svelte +83 -0
  59. package/templates/default/settings.json +1 -3
  60. package/templates/invoice/Template.svelte +247 -0
  61. package/templates/invoice/components/Client.svelte +42 -0
  62. package/templates/invoice/components/Company.svelte +41 -0
  63. package/templates/invoice/components/Items.svelte +66 -0
  64. package/templates/invoice/components/Notes.svelte +21 -0
  65. package/templates/invoice/components/Payment.svelte +25 -0
  66. package/templates/invoice/components/Signature.svelte +34 -0
  67. package/templates/invoice/components/Totals.svelte +47 -0
  68. package/templates/invoice/round.js +2 -0
  69. package/templates/invoice/settings.json +1 -4
  70. package/templates/kilic.dev/Template.svelte +102 -0
  71. package/templates/kilic.dev/settings.json +1 -3
  72. package/templates/mail/Template.svelte +166 -0
  73. package/templates/mail/settings.json +1 -3
  74. package/dist/hooks/not-found.hook.mjs +0 -7
  75. package/dist/hooks/update-notifier.hook.mjs +0 -7
  76. package/dist/interfaces/pupeteer.mjs +0 -1
  77. package/templates/default/main.css +0 -73
  78. package/templates/invoice/.gitignore +0 -1
  79. package/templates/invoice/footer.html +0 -18
  80. package/templates/invoice/tailwind.css +0 -164
  81. package/templates/invoice/template.html.j2 +0 -226
  82. package/templates/kilic.dev/footer.html +0 -26
  83. package/templates/kilic.dev/header.html +0 -16
  84. package/templates/kilic.dev/main.css +0 -74
  85. package/templates/mail/.gitignore +0 -1
  86. package/templates/mail/tailwind.css +0 -145
  87. package/templates/mail/template.html.j2 +0 -14
@@ -1,352 +1,39 @@
1
- import { DEFAULT_LANGUAGE, I18N_DIRECTORY, RequiredTemplateFiles, TEMPLATE_DIRECTORY, TemplateFiles } from "../constants/template.constants.mjs";
2
- import { InputFileType, OutputFileType } from "../constants/file.constants.mjs";
3
- import { I18nService } from "../services/i18n.service.mjs";
4
- import { ATrustAdapter } from "../services/signature/adapters/a-trust.adapter.mjs";
5
- import { SignatureService } from "../services/signature/signature.service.mjs";
6
- import "../services/index.mjs";
7
- import tailwind from "@tailwindcss/postcss";
8
- import { watch } from "chokidar";
9
- import graymatter from "gray-matter";
10
- import { defaultConfig } from "md-to-pdf/dist/lib/config.js";
11
- import { convertMdToPdf } from "md-to-pdf/dist/lib/md-to-pdf.js";
12
- import { serveDirectory } from "md-to-pdf/dist/lib/serve-dir.js";
13
- import Nunjucks from "nunjucks";
14
- import { basename, dirname, extname, isAbsolute, join } from "path";
15
- import postcss from "postcss";
16
- import * as Puppeteer from "puppeteer";
17
- import puppeteer from "puppeteer";
18
- import showdown from "showdown";
19
- import { Args, Command, ConfigService, FileSystemService, Flags, JsonParser, MergeStrategy, ParserService, YamlParser, merge } from "@cenk1cenk2/oclif-common";
20
-
1
+ import __decorate from "../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorate.mjs";
2
+ import { MdPrinterHandler } from "../modules/md-printer/md-printer.handler.mjs";
3
+ import { MdPrinterModule } from "../modules/md-printer/md-printer.module.mjs";
4
+ import { MdPrinterSchema } from "../modules/md-printer/md-printer.schema.mjs";
5
+ import { Args, Command, Flags, UseHandler } from "@cenk1cenk2/oclif-common";
21
6
  //#region src/commands/index.ts
22
- const { parsePDFOptions, unitToPixels } = Puppeteer;
23
- var MDPrinter = class extends Command {
24
- static description = "Generates a PDF from the given markdown file with the selected HTML template.";
7
+ let MDPrinter = class MDPrinter extends Command {
8
+ static description = "Renders a markdown or yaml file through a Svelte template into a PDF or HTML document.";
25
9
  static flags = {
26
- stdin: Flags.string({
27
- char: "I",
28
- description: "Read the input from stdin.",
29
- required: false,
30
- exclusive: ["file"],
31
- allowStdin: "only"
32
- }),
33
- ["input-filetype"]: Flags.string({
34
- char: "f",
35
- options: Object.values(InputFileType),
36
- description: "File type to be processed. By default it is detected by the file extension.",
37
- default: "md"
38
- }),
39
- ["output-filetype"]: Flags.string({
40
- char: "F",
41
- options: Object.values(OutputFileType),
42
- description: "File type to be processed. By default it is detected by the file extension.",
43
- default: "pdf"
44
- }),
45
- stdout: Flags.boolean({
46
- char: "O",
47
- description: "Write to stdout instead of a file."
48
- }),
49
- template: Flags.string({
50
- char: "t",
51
- default: "default",
52
- description: "HTML template for the generated PDF file."
53
- }),
54
- title: Flags.string({
55
- char: "T",
56
- description: "Overwrite document title."
57
- }),
58
- language: Flags.string({
59
- char: "l",
60
- description: "Language code for template translations. Overwrites the one defined in front-matter and template settings."
61
- }),
62
- browser: Flags.string({
63
- char: "b",
64
- description: "Browser path that is going to be used for the PDF generation.",
65
- default: "/usr/bin/brave"
66
- }),
67
- watch: Flags.boolean({
68
- char: "w",
69
- description: "Watch the changes on the given file."
70
- }),
71
- dev: Flags.boolean({
72
- char: "d",
73
- description: "Run with Chrome browser instead of publishing the file."
74
- }),
75
- continuous: Flags.boolean({ description: "Render PDF as a single continuous page without page breaks." }),
76
- sign: Flags.boolean({ description: "Sign the PDF with a qualified electronic signature through ID Austria (A-Trust)." }),
77
- ["sign-provider"]: Flags.string({ description: "Signature provider, defaults to a-trust. Overwrites the one defined in front-matter and template settings." }),
78
- ["sign-certificate"]: Flags.string({ description: "Signer certificate (DER or PEM) used for the visible signature block and to verify the returned signature." })
10
+ stdin: Flags.boolean({ char: "I" }),
11
+ ["input-filetype"]: Flags.string({ char: "f" }),
12
+ ["output-filetype"]: Flags.string({ char: "F" }),
13
+ stdout: Flags.boolean({ char: "O" }),
14
+ template: Flags.string({ char: "t" }),
15
+ title: Flags.string({ char: "T" }),
16
+ language: Flags.string({ char: "l" }),
17
+ browser: Flags.string({ char: "b" }),
18
+ continuous: Flags.boolean(),
19
+ preview: Flags.boolean({ char: "p" }),
20
+ host: Flags.string(),
21
+ port: Flags.integer(),
22
+ open: Flags.boolean({ allowNo: true }),
23
+ sign: Flags.boolean(),
24
+ ["sign-provider"]: Flags.string(),
25
+ ["sign-certificate"]: Flags.string()
79
26
  };
80
27
  static args = {
81
- file: Args.string({ description: "File to be processed." }),
82
- output: Args.string({
83
- description: "Output file that will be generated. Overwrites the one define in front-matter.",
84
- required: false
85
- })
28
+ file: Args.string({ ignoreStdin: true }),
29
+ output: Args.string({ ignoreStdin: true })
86
30
  };
87
- nunjucks = Nunjucks.configure({
88
- autoescape: false,
89
- throwOnUndefined: true,
90
- trimBlocks: true,
91
- lstripBlocks: false
92
- });
93
- cs;
94
- fs;
95
- i18n;
96
- signature;
97
- browser;
98
- register(cli) {
99
- return {
100
- ...cli,
101
- providers: [
102
- ...cli.providers ?? [],
103
- I18nService,
104
- ATrustAdapter,
105
- SignatureService
106
- ]
107
- };
108
- }
109
- async shouldRunBefore() {
110
- this.cs = this.app.get(ConfigService);
111
- this.fs = this.app.get(FileSystemService);
112
- this.i18n = this.app.get(I18nService);
113
- this.signature = this.app.get(SignatureService);
114
- await this.app.get(ParserService).register(JsonParser, YamlParser);
115
- this.nunjucks.addFilter("markdown_to_html", (markdown) => {
116
- return new showdown.Converter().makeHtml(markdown);
117
- });
118
- this.tasks.options = { silentRendererCondition: true };
119
- if (!this.args.file && !this.flags.stdin) throw new Error("Either --stdin or file argument must be provided.");
120
- }
121
- async run() {
122
- this.tasks.add([{ task: async (ctx) => {
123
- if (this.args.file) {
124
- ctx.file = isAbsolute(this.args.file) ? this.args.file : join(process.cwd(), this.args.file);
125
- if (!this.fs.exists(ctx.file)) throw new Error(`File does not exists: ${ctx.file}`);
126
- this.logger.info("Loading file: %s", ctx.file);
127
- ctx.content = await this.fs.read(ctx.file);
128
- } else ctx.content = this.flags.stdin;
129
- switch (this.flags["input-filetype"] ? this.flags["input-filetype"] : extname(ctx.file).replace(/^\./, "")) {
130
- case "md": {
131
- const data = graymatter(ctx.content);
132
- ctx.content = data.content;
133
- ctx.metadata = data.data;
134
- break;
135
- }
136
- case "yaml":
137
- case "yml":
138
- ctx.metadata = await this.app.get(ParserService).parse(ctx.file, ctx.content);
139
- break;
140
- default: throw new Error("File type is not accepted.");
141
- }
142
- } }, { task: async (ctx) => {
143
- const template = ctx.metadata?.template ?? this.flags.template;
144
- ctx.templates = (/* @__PURE__ */ new RegExp(/\.\.?\//)).test(template) ? join(process.cwd(), template) : join(this.config.root, TEMPLATE_DIRECTORY, template);
145
- this.logger.info("Loading template: %s from %s", template, ctx.templates);
146
- await Promise.all(RequiredTemplateFiles.map(async (file) => {
147
- const current = join(ctx.templates, file);
148
- if (!this.fs.exists(current)) throw new Error(`Template does not exists: ${current}`);
149
- }));
150
- const paths = {
151
- ["settings.json"]: join(ctx.templates, "settings.json"),
152
- ["main.css"]: join(ctx.templates, "main.css"),
153
- ["tailwind.css"]: join(ctx.templates, "tailwind.css"),
154
- ["header.html"]: join(ctx.templates, "header.html"),
155
- ["footer.html"]: join(ctx.templates, "footer.html"),
156
- ["template.html.j2"]: join(ctx.templates, "template.html.j2")
157
- };
158
- const title = ctx.metadata?.document_title ?? this.flags.title;
159
- ctx.options = await this.cs.extend([paths["settings.json"], {
160
- dest: this.args?.output ?? ctx.metadata?.dest ?? `${basename(this.args.file, extname(this.args.file))}.${this.flags["output-filetype"]}`,
161
- ...title ? { document_title: title } : {},
162
- ...ctx.metadata?.sign ? { sign: ctx.metadata.sign } : {},
163
- launch_options: { executablePath: this.flags.browser }
164
- }]);
165
- ctx.renderContinuous = this.flags.continuous || ctx.metadata?.continuous === true || ctx.options.continuous === true;
166
- this.flags.stdout ??= ctx.metadata.stdout;
167
- if (this.flags.stdout) ctx.options.dest = "stdout";
168
- if (this.flags["output-filetype"] === "html" || ctx.metadata["output-filetype"] === "html") ctx.options.as_html = true;
169
- this.logger.debug("Options: %o", ctx.options);
170
- if (this.fs.exists(paths["header.html"])) {
171
- this.logger.debug("Header exists for template.");
172
- ctx.options.pdf_options.headerTemplate = await this.fs.read(paths["header.html"]);
173
- }
174
- if (this.fs.exists(paths["footer.html"])) {
175
- this.logger.debug("Footer exists for template.");
176
- ctx.options.pdf_options.footerTemplate = await this.fs.read(paths["footer.html"]);
177
- }
178
- if (this.fs.exists(paths["template.html.j2"])) {
179
- this.logger.debug("Design template exists for template.");
180
- ctx.template = await this.fs.read(paths["template.html.j2"]);
181
- this.logger.debug("Metadata: %o", ctx.metadata);
182
- }
183
- if (this.fs.exists(paths["main.css"])) {
184
- this.logger.debug("CSS exists for template.");
185
- ctx.options.css = await this.fs.read(paths["main.css"]);
186
- }
187
- if (this.fs.exists(paths["tailwind.css"])) {
188
- this.logger.debug("Tailwind CSS exists for template: %s -> %s", paths["tailwind.css"]);
189
- ctx.options.css = await postcss([tailwind({ base: ctx.templates })]).process(await this.fs.read(paths["tailwind.css"]), { from: paths["tailwind.css"] }).then((result) => result.css);
190
- }
191
- await this.applyTranslations(ctx);
192
- await this.applySignature(ctx);
193
- await this.applyDocumentTitle(ctx);
194
- } }]);
195
- }
196
- async shouldRunAfter(ctx) {
197
- if (this.flags.watch) {
198
- this.logger.info("Running in watch mode.");
199
- watch([
200
- TEMPLATE_DIRECTORY,
201
- this.args.file,
202
- join(ctx.templates, "**/*")
203
- ]).on("change", async () => {
204
- await this.run();
205
- const ctx = await this.runTasks();
206
- this.logger.info("Waiting for the next change.");
207
- return this.runMd2Pdf(ctx).catch((err) => {
208
- this.logger.error(err);
209
- });
210
- });
211
- return this.runMd2Pdf(ctx);
212
- }
213
- await this.runMd2Pdf(ctx);
214
- if (this.browser) await this.browser.close();
215
- }
216
- context(ctx) {
217
- const context = {
218
- ...ctx.metadata ?? {},
219
- content: ctx.content,
220
- signature: ctx.signature ?? null
221
- };
222
- if (ctx.t) {
223
- context.t = ctx.t;
224
- context.language = ctx.language;
225
- }
226
- return context;
227
- }
228
- async applyDocumentTitle(ctx) {
229
- const title = ctx.options.document_title ?? this.args.file;
230
- if (!title) return;
231
- ctx.options.document_title = this.nunjucks.renderString(title, this.context(ctx));
232
- this.logger.info("Document title will be: %s", ctx.options.document_title);
233
- }
234
- async applySignature(ctx) {
235
- ctx.options.sign = {
236
- ...ctx.options.sign,
237
- ...this.flags.sign ? { enabled: true } : {},
238
- ...this.flags["sign-provider"] ? { provider: this.flags["sign-provider"] } : {},
239
- ...this.flags["sign-certificate"] ? { certificate: this.flags["sign-certificate"] } : {}
240
- };
241
- if (!ctx.options.sign.enabled) return;
242
- if (ctx.options.as_html) throw new Error("Signing is only supported for PDF output.");
243
- this.signature.adapter(ctx.options.sign.provider);
244
- ctx.signingTime = /* @__PURE__ */ new Date();
245
- if (ctx.options.sign.certificate) ctx.certificate = await this.signature.certificate(ctx.options.sign.certificate, ctx.signingTime);
246
- ctx.signature = this.signature.describe(ctx.signingTime, ctx.certificate);
247
- this.logger.info("Document will be signed: %o", ctx.signature);
248
- }
249
- async applyTranslations(ctx) {
250
- ctx.language = this.flags.language ?? ctx.metadata?.language ?? ctx.options.i18n?.language ?? "en";
251
- ctx.t = await this.i18n.load(join(ctx.templates, I18N_DIRECTORY), {
252
- language: ctx.language,
253
- fallback: ctx.options.i18n?.fallback
254
- });
255
- if (ctx.t) this.logger.info("Rendering with language: %s", ctx.language);
256
- }
257
- continuousBrowser(browser) {
258
- return new Proxy(browser, { get: (target, property, receiver) => {
259
- if (property !== "newPage") {
260
- const value = Reflect.get(target, property, receiver);
261
- return typeof value === "function" ? value.bind(target) : value;
262
- }
263
- return async () => {
264
- const page = await browser.newPage();
265
- const pdf = page.pdf.bind(page);
266
- page.pdf = async (pdfOptions = {}) => {
267
- const parsed = parsePDFOptions(pdfOptions, "cm");
268
- const width = parsed.landscape ? parsed.height : parsed.width;
269
- const margin = parsed.margin;
270
- const viewport = page.viewport();
271
- await page.setViewport({
272
- width: Math.ceil(Math.max(width - margin.left - margin.right, 1) * unitToPixels.cm),
273
- height: viewport?.height ?? 800,
274
- deviceScaleFactor: viewport?.deviceScaleFactor ?? 1
275
- });
276
- const contentHeight = await page.evaluate(async () => {
277
- if ("fonts" in document) await document.fonts.ready;
278
- await Promise.all(Array.from(document.images).map((image) => {
279
- if (image.complete) return;
280
- return new Promise((resolve) => {
281
- image.addEventListener("load", resolve, { once: true });
282
- image.addEventListener("error", resolve, { once: true });
283
- });
284
- }));
285
- const bodyTop = document.body.getBoundingClientRect().top;
286
- const bottom = Math.max(document.body.getBoundingClientRect().bottom, ...Array.from(document.body.querySelectorAll("*")).map((element) => element.getBoundingClientRect().bottom));
287
- return Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.getBoundingClientRect().height, document.documentElement.getBoundingClientRect().height, bottom - bodyTop);
288
- });
289
- const height = parsePDFOptions({ height: Math.ceil(contentHeight + unitToPixels.cm) }, "cm").height + margin.top + margin.bottom;
290
- const options = {
291
- ...pdfOptions,
292
- width: `${width}cm`,
293
- height: `${height}cm`,
294
- landscape: false
295
- };
296
- delete options.format;
297
- return pdf(options);
298
- };
299
- return page;
300
- };
301
- } });
302
- }
303
- async runMd2Pdf(ctx) {
304
- const options = merge(MergeStrategy.EXTEND, { basedir: process.cwd() }, defaultConfig, ctx.options, { devtools: this.flags.dev });
305
- if (!this.browser) {
306
- const browser = await puppeteer.launch({
307
- devtools: options.devtools,
308
- ...options.launch_options
309
- });
310
- this.browser = ctx.renderContinuous ? this.continuousBrowser(browser) : browser;
311
- }
312
- const server = await serveDirectory(ctx.options);
313
- options.port = server.address().port;
314
- let output;
315
- if (ctx.template) {
316
- this.logger.info("Rendering as template.");
317
- output = await convertMdToPdf({ content: this.nunjucks.renderString(ctx.template, this.context(ctx)) }, options, { browser: this.browser });
318
- } else {
319
- this.logger.info("Rendering as plain file.");
320
- output = await convertMdToPdf({ content: ctx.content }, options, { browser: this.browser });
321
- }
322
- await new Promise((resolve, reject) => server.close((err) => {
323
- if (err) return reject(err);
324
- resolve(null);
325
- }));
326
- if (output) {
327
- if (ctx.signature) {
328
- if (this.flags.watch) this.logger.warn("Skipping the signature in watch mode, run without --watch to sign the document.");
329
- else {
330
- output.content = await this.signature.sign(Buffer.from(output.content), {
331
- ...ctx.options.sign,
332
- date: ctx.signingTime,
333
- certificate: ctx.certificate
334
- });
335
- this.logger.info("Document is signed.");
336
- }
337
- }
338
- if (this.flags.stdout) {
339
- process.stdout.write(output.content);
340
- return;
341
- }
342
- if (!output.filename) throw new Error("Output should either be defined with the variable or front-matter.");
343
- this.logger.info("Output file will be: %s", output.filename);
344
- await this.fs.mkdir(dirname(output.filename));
345
- this.logger.info("Writing file to output: %s", output.filename);
346
- await this.fs.write(output.filename, output.content);
347
- }
348
- }
349
31
  };
350
-
32
+ MDPrinter = __decorate([UseHandler({
33
+ module: MdPrinterModule,
34
+ handler: MdPrinterHandler,
35
+ schema: MdPrinterSchema
36
+ })], MDPrinter);
37
+ var commands_default = MDPrinter;
351
38
  //#endregion
352
- export { MDPrinter as default };
39
+ export { commands_default as default };
@@ -0,0 +1 @@
1
+ <span class="date"></span>
@@ -0,0 +1,20 @@
1
+ <!--
2
+ @component
3
+ The markdown document, with directives rendered as components. Styled by github-markdown-css when the template imports it.
4
+ -->
5
+ <script>
6
+ import { getContext } from 'svelte'
7
+
8
+ import Nodes from './Nodes.svelte'
9
+
10
+ /** @type {{ class?: string }} */
11
+ let { class: className = '' } = $props()
12
+ </script>
13
+
14
+ <article class="markdown-body {className}"><Nodes nodes={getContext('md-printer:markdown')} /></article>
15
+
16
+ <style>
17
+ :global(.page-break) {
18
+ break-after: page;
19
+ }
20
+ </style>
@@ -0,0 +1,32 @@
1
+ <!--
2
+ @component
3
+ Resolved markdown: HTML as is, directives as the components registered for them, and the elements around a directive as elements.
4
+ -->
5
+ <script>
6
+ import { getContext } from 'svelte'
7
+
8
+ // eslint-disable-next-line import/no-self-import -- a Svelte 5 component recurses by importing itself
9
+ import Nodes from './Nodes.svelte'
10
+
11
+ /** @type {{ nodes: import('../interfaces/markdown.interface.js').MarkdownNode[] }} */
12
+ let { nodes } = $props()
13
+
14
+ /** @type {Record<string, import('svelte').Component<any>>} */
15
+ const components = getContext('md-printer:components')
16
+ </script>
17
+
18
+ {#each nodes as node, index (index)}
19
+ {#if node.type === 'html'}
20
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -- markdown input is trusted, same as the document itself -->
21
+ {@html node.html}
22
+ {:else if node.type === 'element'}
23
+ <svelte:element this={node.tag} {...node.attributes}><Nodes nodes={node.children} /></svelte:element>
24
+ {:else}
25
+ {@const Component = components[node.name]}
26
+ {#if node.children.length}
27
+ <Component {...node.props}><Nodes nodes={node.children} /></Component>
28
+ {:else}
29
+ <Component {...node.props} />
30
+ {/if}
31
+ {/if}
32
+ {/each}
@@ -0,0 +1,20 @@
1
+ <!--
2
+ @component
3
+ Page layout: renders `children` as the document body and the `header` / `footer` snippets into the printed header and footer of every page.
4
+ -->
5
+ <script>
6
+ import { getContext } from 'svelte'
7
+
8
+ /** @type {{ header?: import('svelte').Snippet, footer?: import('svelte').Snippet, children?: import('svelte').Snippet }} */
9
+ let { header, footer, children } = $props()
10
+
11
+ const mode = getContext('md-printer:mode')
12
+ </script>
13
+
14
+ {#if mode === 'header'}
15
+ {@render header?.()}
16
+ {:else if mode === 'footer'}
17
+ {@render footer?.()}
18
+ {:else}
19
+ {@render children?.()}
20
+ {/if}
@@ -0,0 +1,5 @@
1
+ <!--
2
+ @component
3
+ Starts a new page in the printed PDF. In markdown, `<div class="page-break"></div>` does the same.
4
+ -->
5
+ <div class="page-break" style="break-after: page"></div>
@@ -0,0 +1 @@
1
+ <span class="pageNumber"></span>
@@ -0,0 +1,56 @@
1
+ <!--
2
+ @component
3
+ Visible block for a digitally signed document, filled from the `signature` prop md-printer passes to templates when signing is enabled.
4
+ Styled with Tailwind classes, so the template needs a `<style lang="postcss">` block importing Tailwind.
5
+ -->
6
+ <script>
7
+ /**
8
+ * @type {{
9
+ * signature: { date: string, name?: string, issuer?: string, serial?: string },
10
+ * labels?: { title?: string, by?: string, date?: string, issuer?: string, verify?: string },
11
+ * class?: string
12
+ * }}
13
+ */
14
+ let { signature, labels = {}, class: className = '' } = $props()
15
+
16
+ const text = $derived({
17
+ title: 'Digitally signed',
18
+ by: 'Signed by',
19
+ date: 'Date',
20
+ issuer: 'Certificate',
21
+ verify: 'This document carries a qualified electronic signature. Verify it at',
22
+ ...labels
23
+ })
24
+ </script>
25
+
26
+ <div class="w-full overflow-hidden rounded-xl border-y-2 shadow-md shadow-gray-300 border-gray-900! text-xs {className}">
27
+ <div class="flex items-center gap-3 px-5 py-3 bg-gray-900 text-white">
28
+ <svg class="w-5 h-5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
29
+ <path d="M12 3l7 3v6c0 4.5-3 7.5-7 9-4-1.5-7-4.5-7-9V6l7-3z" />
30
+ <path d="M9 12l2 2 4-4" />
31
+ </svg>
32
+ <span class="font-extrabold uppercase tracking-wider">{text.title}</span>
33
+ </div>
34
+ <div class="flex flex-col px-5 py-2">
35
+ {#if signature.name}
36
+ <div class="flex justify-between gap-4 py-2">
37
+ <span class="font-semibold text-gray-400">{text.by}</span>
38
+ <span class="font-semibold text-right">{signature.name}</span>
39
+ </div>
40
+ {/if}
41
+ <div class="flex justify-between gap-4 py-2 border-t border-t-gray-100/30">
42
+ <span class="font-semibold text-gray-400">{text.date}</span>
43
+ <span class="text-right">{signature.date}</span>
44
+ </div>
45
+ {#if signature.issuer}
46
+ <div class="flex justify-between gap-4 py-2 border-t border-t-gray-100/30">
47
+ <span class="font-semibold text-gray-400">{text.issuer}</span>
48
+ <span class="text-right text-[0.6rem]">{signature.issuer} · {signature.serial}</span>
49
+ </div>
50
+ {/if}
51
+ </div>
52
+ <p class="px-5 py-3 bg-gray-50 text-[0.6rem] text-gray-400 leading-snug">
53
+ {text.verify}
54
+ <a href="https://www.signaturpruefung.gv.at" class="font-semibold text-gray-900 underline">signaturpruefung.gv.at</a>
55
+ </p>
56
+ </div>
@@ -0,0 +1 @@
1
+ <span class="title"></span>
@@ -0,0 +1 @@
1
+ <span class="totalPages"></span>
@@ -0,0 +1,8 @@
1
+ export { default as Date } from './Date.svelte'
2
+ export { default as Markdown } from './Markdown.svelte'
3
+ export { default as Page } from './Page.svelte'
4
+ export { default as PageBreak } from './PageBreak.svelte'
5
+ export { default as PageNumber } from './PageNumber.svelte'
6
+ export { default as SignatureBlock } from './SignatureBlock.svelte'
7
+ export { default as Title } from './Title.svelte'
8
+ export { default as TotalPages } from './TotalPages.svelte'
@@ -1,10 +1,4 @@
1
1
  //#region src/constants/file.constants.ts
2
- const OUTPUT_FILE_ACCEPTED_TYPES = [".pdf"];
3
- const INPUT_FILE_ACCEPTED_TYPES = [
4
- ".md",
5
- ".yml",
6
- ".yaml"
7
- ];
8
2
  let OutputFileType = /* @__PURE__ */ function(OutputFileType) {
9
3
  OutputFileType["PDF"] = "pdf";
10
4
  OutputFileType["HTML"] = "html";
@@ -16,6 +10,5 @@ let InputFileType = /* @__PURE__ */ function(InputFileType) {
16
10
  InputFileType["YAML_SHORT"] = "yml";
17
11
  return InputFileType;
18
12
  }({});
19
-
20
13
  //#endregion
21
- export { INPUT_FILE_ACCEPTED_TYPES, InputFileType, OUTPUT_FILE_ACCEPTED_TYPES, OutputFileType };
14
+ export { InputFileType, OutputFileType };
@@ -1,4 +1,5 @@
1
- import { DEFAULT_LANGUAGE, I18N_ACCEPTED_TYPES, I18N_DIRECTORY, RequiredTemplateFiles, TEMPLATE_DIRECTORY, TemplateFiles } from "./template.constants.mjs";
2
- import { INPUT_FILE_ACCEPTED_TYPES, InputFileType, OUTPUT_FILE_ACCEPTED_TYPES, OutputFileType } from "./file.constants.mjs";
3
-
4
- export { DEFAULT_LANGUAGE, I18N_ACCEPTED_TYPES, I18N_DIRECTORY, INPUT_FILE_ACCEPTED_TYPES, InputFileType, OUTPUT_FILE_ACCEPTED_TYPES, OutputFileType, RequiredTemplateFiles, TEMPLATE_DIRECTORY, TemplateFiles };
1
+ import { ContextKey, DEFAULT_HIGHLIGHT_STYLE, DEFAULT_LANGUAGE, DEFAULT_PDF_OPTIONS, DEFAULT_TEMPLATE, DIRECTIVE_TAG, Directory, I18N_ACCEPTED_TYPES, MediaType, ModuleSpecifier, PAGE_BREAK_CLASS, PRINTER_MODULE_OPTIONS, RenderMode, TAILWIND_STYLE_LANG, TemplateFiles } from "./template.constants.mjs";
2
+ import { InputFileType, OutputFileType } from "./file.constants.mjs";
3
+ import { PreviewHost } from "./preview.constants.mjs";
4
+ import { SignatureProvider } from "./signature.constants.mjs";
5
+ export { ContextKey, DEFAULT_HIGHLIGHT_STYLE, DEFAULT_LANGUAGE, DEFAULT_PDF_OPTIONS, DEFAULT_TEMPLATE, DIRECTIVE_TAG, Directory, I18N_ACCEPTED_TYPES, InputFileType, MediaType, ModuleSpecifier, OutputFileType, PAGE_BREAK_CLASS, PRINTER_MODULE_OPTIONS, PreviewHost, RenderMode, SignatureProvider, TAILWIND_STYLE_LANG, TemplateFiles };
@@ -0,0 +1,9 @@
1
+ //#region src/constants/preview.constants.ts
2
+ let PreviewHost = /* @__PURE__ */ function(PreviewHost) {
3
+ PreviewHost["IPV4"] = "127.0.0.1";
4
+ PreviewHost["LOCALHOST"] = "localhost";
5
+ PreviewHost["IPV6"] = "::1";
6
+ return PreviewHost;
7
+ }({});
8
+ //#endregion
9
+ export { PreviewHost };
@@ -0,0 +1,7 @@
1
+ //#region src/constants/signature.constants.ts
2
+ let SignatureProvider = /* @__PURE__ */ function(SignatureProvider) {
3
+ SignatureProvider["A_TRUST"] = "a-trust";
4
+ return SignatureProvider;
5
+ }({});
6
+ //#endregion
7
+ export { SignatureProvider };