thibaud frere commited on
Commit
33aefb9
·
1 Parent(s): c7b266f

update pdf

Browse files
app/scripts/export-pdf.mjs CHANGED
@@ -239,6 +239,61 @@ async function main() {
239
  }
240
  await page.emulateMedia({ media: 'print' });
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  // Generate OG thumbnail (1200x630)
243
  try {
244
  const ogW = 1200, ogH = 630;
@@ -281,13 +336,84 @@ async function main() {
281
  await page.evaluate(() => { window.scrollTo(0, 0); window.dispatchEvent(new Event('resize')); });
282
  try { await waitForD3(page, 8000); } catch {}
283
  await waitForStableLayout(page);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  } catch {}
285
- // Temporarily make D3 banner reliably visible for PDF
286
  let pdfCssHandle = null;
287
  try {
288
  pdfCssHandle = await page.addStyleTag({ content: `
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  .hero .points { mix-blend-mode: normal !important; }
290
- .d3-galaxy svg { background: var(--surface-bg); }
 
291
  ` });
292
  } catch {}
293
  await page.pdf({
 
239
  }
240
  await page.emulateMedia({ media: 'print' });
241
 
242
+ // Enforce responsive sizing for SVG/iframes by removing hard attrs and injecting CSS (top-level and inside same-origin iframes)
243
+ try {
244
+ await page.evaluate(() => {
245
+ function isSmallSvg(svg){
246
+ try {
247
+ const vb = svg && svg.viewBox && svg.viewBox.baseVal ? svg.viewBox.baseVal : null;
248
+ if (vb && vb.width && vb.height && vb.width <= 50 && vb.height <= 50) return true;
249
+ const r = svg.getBoundingClientRect && svg.getBoundingClientRect();
250
+ if (r && r.width && r.height && r.width <= 50 && r.height <= 50) return true;
251
+ } catch {}
252
+ return false;
253
+ }
254
+ function lockSmallSvgSize(svg){
255
+ try {
256
+ const r = svg.getBoundingClientRect ? svg.getBoundingClientRect() : null;
257
+ const w = (r && r.width) ? Math.round(r.width) : null;
258
+ const h = (r && r.height) ? Math.round(r.height) : null;
259
+ if (w) svg.style.setProperty('width', w + 'px', 'important');
260
+ if (h) svg.style.setProperty('height', h + 'px', 'important');
261
+ svg.style.setProperty('max-width', 'none', 'important');
262
+ } catch {}
263
+ }
264
+ function fixSvg(svg){
265
+ if (!svg) return;
266
+ if (isSmallSvg(svg)) { lockSmallSvgSize(svg); return; }
267
+ try { svg.removeAttribute('width'); } catch {}
268
+ try { svg.removeAttribute('height'); } catch {}
269
+ svg.style.maxWidth = '100%';
270
+ svg.style.width = '100%';
271
+ svg.style.height = 'auto';
272
+ if (!svg.getAttribute('preserveAspectRatio')) svg.setAttribute('preserveAspectRatio','xMidYMid meet');
273
+ }
274
+ document.querySelectorAll('svg').forEach(fixSvg);
275
+ document.querySelectorAll('.mermaid, .mermaid svg').forEach((el)=>{
276
+ if (el.tagName && el.tagName.toLowerCase() === 'svg') fixSvg(el);
277
+ else { el.style.display='block'; el.style.width='100%'; el.style.maxWidth='100%'; }
278
+ });
279
+ document.querySelectorAll('iframe, embed, object').forEach((el) => {
280
+ el.style.width = '100%';
281
+ el.style.maxWidth = '100%';
282
+ try { el.removeAttribute('width'); } catch {}
283
+ // Best-effort inject into same-origin frames
284
+ try {
285
+ const doc = (el.tagName.toLowerCase()==='object' ? el.contentDocument : el.contentDocument);
286
+ if (doc && doc.head) {
287
+ const s = doc.createElement('style');
288
+ s.textContent = 'html,body{overflow-x:hidden;} svg,canvas,img,video{max-width:100%!important;height:auto!important;} svg[width]{width:100%!important}';
289
+ doc.head.appendChild(s);
290
+ doc.querySelectorAll('svg').forEach((svg)=>{ if (isSmallSvg(svg)) lockSmallSvgSize(svg); else fixSvg(svg); });
291
+ }
292
+ } catch (_) { /* cross-origin; ignore */ }
293
+ });
294
+ });
295
+ } catch {}
296
+
297
  // Generate OG thumbnail (1200x630)
298
  try {
299
  const ogW = 1200, ogH = 630;
 
336
  await page.evaluate(() => { window.scrollTo(0, 0); window.dispatchEvent(new Event('resize')); });
337
  try { await waitForD3(page, 8000); } catch {}
338
  await waitForStableLayout(page);
339
+ // Re-apply responsive fixes after viewport change
340
+ try {
341
+ await page.evaluate(() => {
342
+ function isSmallSvg(svg){
343
+ try {
344
+ const vb = svg && svg.viewBox && svg.viewBox.baseVal ? svg.viewBox.baseVal : null;
345
+ if (vb && vb.width && vb.height && vb.width <= 50 && vb.height <= 50) return true;
346
+ const r = svg.getBoundingClientRect && svg.getBoundingClientRect();
347
+ if (r && r.width && r.height && r.width <= 50 && r.height <= 50) return true;
348
+ } catch {}
349
+ return false;
350
+ }
351
+ function lockSmallSvgSize(svg){
352
+ try {
353
+ const r = svg.getBoundingClientRect ? svg.getBoundingClientRect() : null;
354
+ const w = (r && r.width) ? Math.round(r.width) : null;
355
+ const h = (r && r.height) ? Math.round(r.height) : null;
356
+ if (w) svg.style.setProperty('width', w + 'px', 'important');
357
+ if (h) svg.style.setProperty('height', h + 'px', 'important');
358
+ svg.style.setProperty('max-width', 'none', 'important');
359
+ } catch {}
360
+ }
361
+ function fixSvg(svg){
362
+ if (!svg) return;
363
+ if (isSmallSvg(svg)) { lockSmallSvgSize(svg); return; }
364
+ try { svg.removeAttribute('width'); } catch {}
365
+ try { svg.removeAttribute('height'); } catch {}
366
+ svg.style.maxWidth = '100%';
367
+ svg.style.width = '100%';
368
+ svg.style.height = 'auto';
369
+ if (!svg.getAttribute('preserveAspectRatio')) svg.setAttribute('preserveAspectRatio','xMidYMid meet');
370
+ }
371
+ document.querySelectorAll('svg').forEach((svg)=>{ if (isSmallSvg(svg)) lockSmallSvgSize(svg); else fixSvg(svg); });
372
+ document.querySelectorAll('.mermaid, .mermaid svg').forEach((el)=>{
373
+ if (el.tagName && el.tagName.toLowerCase() === 'svg') fixSvg(el);
374
+ else { el.style.display='block'; el.style.width='100%'; el.style.maxWidth='100%'; }
375
+ });
376
+ document.querySelectorAll('iframe, embed, object').forEach((el) => {
377
+ el.style.width = '100%';
378
+ el.style.maxWidth = '100%';
379
+ try { el.removeAttribute('width'); } catch {}
380
+ try {
381
+ const doc = (el.tagName.toLowerCase()==='object' ? el.contentDocument : el.contentDocument);
382
+ if (doc && doc.head) {
383
+ const s = doc.createElement('style');
384
+ s.textContent = 'html,body{overflow-x:hidden;} svg,canvas,img,video{max-width:100%!important;height:auto!important;} svg[width]{width:100%!important}';
385
+ doc.head.appendChild(s);
386
+ doc.querySelectorAll('svg').forEach((svg)=>{ if (isSmallSvg(svg)) lockSmallSvgSize(svg); else fixSvg(svg); });
387
+ }
388
+ } catch (_) {}
389
+ });
390
+ });
391
+ } catch {}
392
  } catch {}
393
+ // Temporarily enforce print-safe responsive sizing (SVG/iframes) and improve banner visibility
394
  let pdfCssHandle = null;
395
  try {
396
  pdfCssHandle = await page.addStyleTag({ content: `
397
+ /* General container safety */
398
+ html, body { overflow-x: hidden !important; }
399
+
400
+ /* Make all vector/bitmap media responsive for print */
401
+ svg, canvas, img, video { max-width: 100% !important; height: auto !important; }
402
+ /* Mermaid diagrams */
403
+ .mermaid, .mermaid svg { display: block; width: 100% !important; max-width: 100% !important; height: auto !important; }
404
+ /* Any explicit width attributes */
405
+ svg[width] { width: 100% !important; }
406
+ /* Iframes and similar embeds */
407
+ iframe, embed, object { width: 100% !important; max-width: 100% !important; height: auto; }
408
+
409
+ /* HtmlEmbed wrappers (defensive) */
410
+ .html-embed, .html-embed__card { max-width: 100% !important; width: 100% !important; }
411
+ .html-embed__card > div[id^="frag-"] { width: 100% !important; max-width: 100% !important; }
412
+
413
+ /* Banner centering & visibility */
414
  .hero .points { mix-blend-mode: normal !important; }
415
+ .d3-galaxy { width: 100% !important; height: 300px; max-width: 980px !important; margin-left: auto !important; margin-right: auto !important; }
416
+ .d3-galaxy svg { background: var(--surface-bg); width: 100% !important; height: auto !important; }
417
  ` });
418
  } catch {}
419
  await page.pdf({
app/src/components/Hero.astro CHANGED
@@ -57,7 +57,7 @@ const pdfFilename = `${slugify(pdfBase)}.pdf`;
57
  <p>{published}</p>
58
  </div>
59
  )}
60
- <div class="meta-container-cell">
61
  <h3>PDF</h3>
62
  <p><button id="download-pdf-btn" data-pdf-filename={pdfFilename}>Download PDF</button></p>
63
  </div>
@@ -99,6 +99,7 @@ const pdfFilename = `${slugify(pdfBase)}.pdf`;
99
  .meta-container-cell { display: flex; flex-direction: column; gap: 8px; }
100
  .meta-container-cell h3 { margin: 0; font-size: 12px; font-weight: 400; color: var(--muted-color); text-transform: uppercase; letter-spacing: .02em; }
101
  .meta-container-cell p { margin: 0; }
 
102
  </style>
103
 
104
 
 
57
  <p>{published}</p>
58
  </div>
59
  )}
60
+ <div class="meta-container-cell meta-container-cell--pdf">
61
  <h3>PDF</h3>
62
  <p><button id="download-pdf-btn" data-pdf-filename={pdfFilename}>Download PDF</button></p>
63
  </div>
 
99
  .meta-container-cell { display: flex; flex-direction: column; gap: 8px; }
100
  .meta-container-cell h3 { margin: 0; font-size: 12px; font-weight: 400; color: var(--muted-color); text-transform: uppercase; letter-spacing: .02em; }
101
  .meta-container-cell p { margin: 0; }
102
+ @media print { .meta-container-cell--pdf { display: none !important; } }
103
  </style>
104
 
105
 
app/src/components/HtmlEmbed.astro CHANGED
@@ -102,7 +102,20 @@ const mountId = `frag-${Math.random().toString(36).slice(2)}`;
102
  .html-embed__card > div[id^="frag-"] { width: 100% !important; }
103
  }
104
  @media print {
 
105
  .html-embed, .html-embed__card { break-inside: avoid; page-break-inside: avoid; }
 
 
 
 
 
 
 
 
 
 
 
 
106
  }
107
  </style>
108
 
 
102
  .html-embed__card > div[id^="frag-"] { width: 100% !important; }
103
  }
104
  @media print {
105
+ /* Avoid breaks inside embeds */
106
  .html-embed, .html-embed__card { break-inside: avoid; page-break-inside: avoid; }
107
+ /* Constrain width and scale inner content */
108
+ .html-embed, .html-embed__card { max-width: 100% !important; width: 100% !important; }
109
+ .html-embed__card { padding: 6px; }
110
+ .html-embed__card.is-frameless { padding: 0; }
111
+ .html-embed__card svg,
112
+ .html-embed__card canvas,
113
+ .html-embed__card img,
114
+ .html-embed__card video,
115
+ .html-embed__card iframe { max-width: 100% !important; height: auto !important; }
116
+ .html-embed__card > div[id^="frag-"] { width: 100% !important; max-width: 100% !important; }
117
+ /* Center and constrain the banner (galaxy) when printing */
118
+ .html-embed .d3-galaxy { width: 100% !important; max-width: 980px !important; margin-left: auto !important; margin-right: auto !important; }
119
  }
120
  </style>
121
 
app/src/content/embeds/palettes.html CHANGED
@@ -28,11 +28,11 @@
28
  .palettes input[type="range"]:focus { outline: none; }
29
  /* WebKit */
30
  .palettes input[type="range"]::-webkit-slider-runnable-track { height: 6px; background: var(--border-color); border-radius: 999px; }
31
- .palettes input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; margin-top: -6px; width: 18px; height: 18px; background: var(--primary-color); border: 2px solid var(--surface-bg); border-radius: 50%; box-shadow: 0 1px 2px rgba(0,0,0,.15); }
32
  /* Firefox */
33
  .palettes input[type="range"]::-moz-range-track { height: 6px; background: var(--border-color); border: none; border-radius: 999px; }
34
  .palettes input[type="range"]::-moz-range-progress { height: 6px; background: var(--primary-color); border-radius: 999px; }
35
- .palettes input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; background: var(--primary-color); border: 2px solid var(--surface-bg); border-radius: 50%; box-shadow: 0 1px 2px rgba(0,0,0,.15); }
36
  /* Page-wide color vision simulation classes */
37
  html.cb-grayscale, body.cb-grayscale { filter: grayscale(1) !important; }
38
  html.cb-protanopia, body.cb-protanopia { filter: url(#cb-protanopia) !important; }
 
28
  .palettes input[type="range"]:focus { outline: none; }
29
  /* WebKit */
30
  .palettes input[type="range"]::-webkit-slider-runnable-track { height: 6px; background: var(--border-color); border-radius: 999px; }
31
+ .palettes input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; margin-top: -6px; width: 18px; height: 18px; background: var(--primary-color); border: 2px solid var(--surface-bg); border-radius: 50%; }
32
  /* Firefox */
33
  .palettes input[type="range"]::-moz-range-track { height: 6px; background: var(--border-color); border: none; border-radius: 999px; }
34
  .palettes input[type="range"]::-moz-range-progress { height: 6px; background: var(--primary-color); border-radius: 999px; }
35
+ .palettes input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; background: var(--primary-color); border: 2px solid var(--surface-bg); border-radius: 50%; }
36
  /* Page-wide color vision simulation classes */
37
  html.cb-grayscale, body.cb-grayscale { filter: grayscale(1) !important; }
38
  html.cb-protanopia, body.cb-protanopia { filter: url(#cb-protanopia) !important; }