ghost-paper 0.3.0 → 0.3.2

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.
@@ -2,7 +2,7 @@ import {
2
2
  build,
3
3
  buildPdf,
4
4
  buildPrintHtml
5
- } from "./chunk-B7BSOHDL.js";
5
+ } from "./chunk-VH6KYYQG.js";
6
6
  export {
7
7
  build,
8
8
  buildPdf,
@@ -284,6 +284,10 @@ function parse(markdown) {
284
284
  }
285
285
  continue;
286
286
  }
287
+ if (node.type === "thematicBreak") {
288
+ currentTab.elements.push({ kind: "pagebreak" });
289
+ continue;
290
+ }
287
291
  if (node.type === "blockquote") {
288
292
  const html = blockquoteToHtml(node);
289
293
  currentTab.elements.push({ kind: "aside", html });
@@ -957,6 +961,12 @@ var CSS = `
957
961
  color: var(--text-primary);
958
962
  }
959
963
 
964
+ hr.pagebreak {
965
+ border: none;
966
+ border-top: 1px solid var(--border);
967
+ margin: 32px 0;
968
+ }
969
+
960
970
  .tab-content .footnote {
961
971
  font-size: 13px;
962
972
  color: var(--text-muted);
@@ -1128,31 +1138,26 @@ var PRINT_CSS = `
1128
1138
  .sidebar, .mobile-header, .mobile-dropdown, .mobile-overlay { display: none !important; }
1129
1139
  .main { margin-left: 0; min-height: auto; }
1130
1140
 
1131
- /* \u2500\u2500 Cover page \u2014 full bleed dark background \u2500\u2500 */
1141
+ /* \u2500\u2500 Cover page \u2014 full bleed dark background, content at bottom \u2500\u2500 */
1132
1142
  .print-header {
1133
1143
  min-height: 100vh;
1134
1144
  display: flex;
1135
1145
  flex-direction: column;
1136
- justify-content: flex-start;
1146
+ justify-content: flex-end;
1137
1147
  background: var(--charcoal);
1138
1148
  margin: -0.7in -0.65in 0;
1139
- padding: calc(0.7in + 48px) calc(0.65in + 48px) 56px;
1149
+ padding: 56px calc(0.65in + 48px) calc(0.7in + 56px);
1140
1150
  page-break-after: always;
1141
1151
  break-after: page;
1142
1152
  }
1143
1153
  .print-title {
1144
- font-size: 96px;
1154
+ font-size: 72px;
1145
1155
  font-weight: 700;
1146
- letter-spacing: -4px;
1156
+ letter-spacing: -3px;
1147
1157
  color: #fff;
1148
- padding-bottom: 28px;
1158
+ padding-bottom: 24px;
1149
1159
  border-bottom: 5px solid var(--accent);
1150
- line-height: 0.95;
1151
- word-spacing: 100vw;
1152
- }
1153
- .print-title .title-minor {
1154
- color: rgba(255,255,255,0.4);
1155
- font-weight: 400;
1160
+ line-height: 1.05;
1156
1161
  }
1157
1162
  .print-subtitle {
1158
1163
  font-size: 15px;
@@ -1225,19 +1230,28 @@ var PRINT_CSS = `
1225
1230
  }
1226
1231
 
1227
1232
  /* Tables: in-column with light grey box */
1228
- .tab-content table {
1229
- font-size: 10px;
1233
+ .table-wrap {
1234
+ overflow: hidden;
1230
1235
  margin: 12px 0;
1231
1236
  break-inside: avoid;
1232
- width: 100%;
1233
1237
  background: #F6F6F4;
1238
+ border-radius: 4px;
1239
+ }
1240
+ .table-wrap.table-wide { column-span: all; }
1241
+ .tab-content table {
1242
+ font-size: 10px;
1243
+ margin: 0;
1244
+ width: 100%;
1245
+ table-layout: fixed;
1234
1246
  border-collapse: separate;
1235
1247
  border-spacing: 0;
1236
- padding: 16px;
1237
- border-radius: 4px;
1248
+ padding: 0;
1249
+ background: transparent;
1250
+ border-radius: 0;
1238
1251
  }
1239
- .tab-content thead th { font-size: 8px; letter-spacing: 0.8px; padding: 5px 8px 5px 0; border-bottom: 2px solid #D0D0CB; }
1240
- .tab-content tbody td { padding: 6px 8px 6px 0; font-weight: 400; border-bottom: 1px solid #E8E8E4; }
1252
+ .tab-content tbody td { word-wrap: break-word; overflow-wrap: break-word; }
1253
+ .tab-content thead th { font-size: 8px; letter-spacing: 0.8px; padding: 8px 10px; border-bottom: 2px solid #D0D0CB; }
1254
+ .tab-content tbody td { padding: 8px 10px; font-weight: 400; border-bottom: 1px solid #E8E8E4; }
1241
1255
  .tab-content tbody tr:last-child td { border-bottom: none; }
1242
1256
 
1243
1257
  /* KPIs: compact grid for column width */
@@ -1263,10 +1277,19 @@ var PRINT_CSS = `
1263
1277
  .chart-container { height: 190px; overflow: hidden; }
1264
1278
 
1265
1279
  .kpi-strip { break-inside: avoid; page-break-inside: avoid; }
1266
- table { break-inside: auto; }
1267
1280
  thead { display: table-header-group; }
1268
1281
  tr { break-inside: avoid; }
1269
1282
 
1283
+ hr.pagebreak {
1284
+ border: none;
1285
+ margin: 0;
1286
+ padding: 0;
1287
+ height: 0;
1288
+ column-span: all;
1289
+ break-after: page;
1290
+ page-break-after: always;
1291
+ }
1292
+
1270
1293
  /* First appendix section starts on a new page */
1271
1294
  .tab-appendix-start { page-break-before: always; break-before: page; }
1272
1295
 
@@ -1333,12 +1356,13 @@ function renderTable(table) {
1333
1356
  }).join("");
1334
1357
  return `<tr>${cells}</tr>`;
1335
1358
  }).join("\n ");
1336
- return ` <table>
1359
+ const wideClass = table.headers.length >= 5 ? " table-wide" : "";
1360
+ return ` <div class="table-wrap${wideClass}"><table>
1337
1361
  <thead><tr>${thead}</tr></thead>
1338
1362
  <tbody>
1339
1363
  ${tbody}
1340
1364
  </tbody>
1341
- </table>`;
1365
+ </table></div>`;
1342
1366
  }
1343
1367
  function renderChart(chartId, caption, chartType, rowCount) {
1344
1368
  let styleAttr = "";
@@ -1365,6 +1389,8 @@ function renderElement(el) {
1365
1389
  return renderTable(el.table);
1366
1390
  case "aside":
1367
1391
  return ` <aside>${el.html}</aside>`;
1392
+ case "pagebreak":
1393
+ return ` <hr class="pagebreak">`;
1368
1394
  }
1369
1395
  }
1370
1396
  function buildSidebar(doc) {
@@ -1447,32 +1473,11 @@ function buildChartScript(doc, printMode = false) {
1447
1473
  if (printMode) return script + "\nwindow.__ghostPaperChartsReady = true;";
1448
1474
  return script;
1449
1475
  }
1450
- function stylizePrintTitle(raw) {
1451
- const MINOR = /* @__PURE__ */ new Set(["the", "a", "an", "for", "of", "in", "and", "or", "to", "with", "by", "on", "at", "is"]);
1452
- const words = escapeHtml3(raw).split(/\s+/);
1453
- const result = [];
1454
- let i = 0;
1455
- while (i < words.length) {
1456
- const lower = words[i].toLowerCase();
1457
- if (i + 1 < words.length && MINOR.has(lower) && MINOR.has(words[i + 1].toLowerCase())) {
1458
- result.push(`<span class="title-minor" style="white-space:nowrap;word-spacing:normal">${words[i]} ${words[i + 1]}</span>`);
1459
- i += 2;
1460
- } else if (MINOR.has(lower)) {
1461
- result.push(`<span class="title-minor">${words[i]}</span>`);
1462
- i++;
1463
- } else {
1464
- result.push(words[i]);
1465
- i++;
1466
- }
1467
- }
1468
- return result.join(" ");
1469
- }
1470
1476
  function buildPrintContent(doc) {
1471
1477
  const subtitle = doc.frontmatter.subtitle ? `
1472
1478
  <div class="print-subtitle">${escapeHtml3(doc.frontmatter.subtitle)}</div>` : "";
1473
- const styledTitle = stylizePrintTitle(doc.frontmatter.title);
1474
1479
  const header = ` <div class="print-header">
1475
- <div class="print-title">${styledTitle}</div>${subtitle}
1480
+ <div class="print-title">${escapeHtml3(doc.frontmatter.title)}</div>${subtitle}
1476
1481
  </div>`;
1477
1482
  const tabs = doc.tabs.map((tab, i) => {
1478
1483
  const elements = tab.elements.map(renderElement).join("\n\n");
@@ -1517,7 +1522,7 @@ function buildPrintHtml(markdown) {
1517
1522
  async function buildPdf(markdown, outputPath, options) {
1518
1523
  const doc = parse(markdown);
1519
1524
  const html = render(doc, { printMode: true });
1520
- const { generatePdf } = await import("./pdf-PXNSBFN6.js");
1525
+ const { generatePdf } = await import("./pdf-CVTVSJBX.js");
1521
1526
  await generatePdf({
1522
1527
  html,
1523
1528
  outputPath,
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  build
4
- } from "./chunk-B7BSOHDL.js";
4
+ } from "./chunk-VH6KYYQG.js";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
@@ -72,7 +72,7 @@ buildCmd.command("pdf").description("Convert a markdown file to a PDF report").a
72
72
  const inputPath = resolve(input);
73
73
  const markdown = readFileSync(inputPath, "utf-8");
74
74
  const outputPath = opts.output ? resolve(opts.output) : inputPath.replace(/\.md$/, ".pdf");
75
- const { buildPdf } = await import("./-G2AWPUXZ.js");
75
+ const { buildPdf } = await import("./-3QXRJ2CJ.js");
76
76
  await buildPdf(markdown, outputPath, {
77
77
  pageSize: opts.pageSize ?? "A4",
78
78
  landscape: opts.landscape ?? false
@@ -52,8 +52,9 @@ async function generatePdf(opts) {
52
52
  });
53
53
  await new Promise((r) => setTimeout(r, 300));
54
54
  const title = escapeHtml(opts.title ?? "");
55
- const headerTemplate = `<div style="font-size: 8px; font-family: -apple-system, system-ui, sans-serif; color: #999; width: 100%; padding: 0 0.65in; letter-spacing: 0.5px; text-transform: uppercase;">${title}</div>`;
56
- const footerTemplate = `<div style="font-size: 8px; font-family: -apple-system, system-ui, sans-serif; color: #999; width: 100%; text-align: right; padding: 0 0.65in;"><span class="pageNumber"></span> / <span class="totalPages"></span></div>`;
55
+ const hideOnPage1 = `<script>if(document.querySelector('.pageNumber').textContent==='1')document.querySelector('#c').style.display='none';</script>`;
56
+ const headerTemplate = `<div style="font-size: 8px; font-family: -apple-system, system-ui, sans-serif; color: #999; width: 100%; padding: 0 0.65in; letter-spacing: 0.5px; text-transform: uppercase;"><span id="c">${title}</span>${hideOnPage1}</div>`;
57
+ const footerTemplate = `<div style="font-size: 8px; font-family: -apple-system, system-ui, sans-serif; color: #999; width: 100%; text-align: right; padding: 0 0.65in;"><span id="c"><span class="pageNumber"></span> / <span class="totalPages"></span></span>${hideOnPage1}</div>`;
57
58
  const pdfBuffer = await page.pdf({
58
59
  format: opts.pageSize ?? "A4",
59
60
  landscape: opts.landscape ?? false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost-paper",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Turn markdown into beautiful HTML reports",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",