@ammduncan/easel 0.2.15 → 0.2.17

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to easel. This project adheres to [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## 0.2.17 — 2026-05-23
6
+
7
+ ### Added
8
+ - **`kind: "mockup"` / `"app"` pushes now break out to near-full viewport width.** Two width bounds applied to every push: (1) the wrapper's presentation frame (`max-width: 1400px` + body padding + 880px prose cap), removed by app-fidelity mode since 0.2.13; (2) the feed reading column (`min(94vw, 1600px)` ≈ ~1540px usable), which still squeezed desktop UI recreations. Now app-fidelity cards get `.push--full-bleed` — `width: min(98vw, 1920px)` centred on the viewport — so desktop screens render at real desktop proportions instead of being pinched into the reading column. Presentation pushes stay in the column as before.
9
+
10
+ ## 0.2.16 — 2026-05-23
11
+
12
+ ### Changed
13
+ - **PDF export quality bumped back up after the 0.2.15 size fix landed.** 0.2.15 traded crispness for size (JPEG 0.92 + DPR 2 → ~800 KB per card) — visible JPEG artefacts on type at zoom. Bumped to JPEG quality 1.0 + DPR 4 for the PDF target. Same compression trick (JPEG-in-PDF vs PNG-in-PDF) keeps the file at ~3–8 MB per card instead of the original 300+ MB; the higher quality settings just bring text back to "razor sharp at any zoom" without giving up the size win.
14
+ - PNG export unchanged (still lossless PNG @ DPR 4).
15
+
5
16
  ## 0.2.15 — 2026-05-23
6
17
 
7
18
  ### Fixed
@@ -501,6 +501,18 @@ body {
501
501
  animation: pop-in 360ms cubic-bezier(0.2, 0.7, 0.2, 1);
502
502
  }
503
503
 
504
+ /* App-fidelity pushes (kind: mockup / app) break out of the feed column to
505
+ near-full viewport width, so desktop UI recreations render at real desktop
506
+ proportions instead of being squeezed to the ~1540px reading column.
507
+ margin-left:50% moves the left edge to the column's centre line (which is
508
+ the viewport centre, since .feed is margin:0 auto), then translateX(-50%)
509
+ pulls back by half the element's own width → viewport-centred full bleed. */
510
+ .push--full-bleed {
511
+ width: min(98vw, 1920px);
512
+ margin-left: 50%;
513
+ transform: translateX(-50%);
514
+ }
515
+
504
516
  @keyframes pop-in {
505
517
  from { opacity: 0; transform: translateY(10px); }
506
518
  to { opacity: 1; transform: none; }
@@ -426,6 +426,11 @@
426
426
  const card = document.createElement("article");
427
427
  card.className = "push";
428
428
  if (opts && opts.fresh) card.classList.add("fresh");
429
+ // App-fidelity pushes (mockup / app) break out of the feed column to
430
+ // near-full viewport width so desktop screens render at real proportions.
431
+ if (push.kind === "mockup" || push.kind === "app") {
432
+ card.classList.add("push--full-bleed");
433
+ }
429
434
  card.id = "push-" + push.id;
430
435
 
431
436
  const meta = document.createElement("div");
@@ -867,18 +872,21 @@ ${body}
867
872
  document.body ? document.body.scrollHeight : 0,
868
873
  );
869
874
  // PNG target → lossless PNG @ pixelRatio 4 for crisp standalone files.
870
- // PDF target → JPEG @ quality 0.92 + pixelRatio 2. PDFs natively use
871
- // DCT compression for embedded images, so JPEG-in-PDF stays small;
872
- // PNG-in-PDF balloons (a tall card at DPR 4 produces 300+ MB PDFs).
875
+ // PDF target → JPEG @ quality 1.0 + pixelRatio 4. PDFs natively use
876
+ // DCT compression for embedded JPEGs, so even at max quality the PDF
877
+ // stays in the ~3-8 MB range for a typical card (vs ~300 MB if we
878
+ // embedded as PNG — see 0.2.15). 1.0 + DPR 4 keeps text razor-sharp
879
+ // at any zoom level; tuned down to 0.92 + DPR 2 in 0.2.15 dropped to
880
+ // ~800 KB but at the cost of visible JPEG artefacts on type.
873
881
  var rasterFn = format === "pdf" ? window.htmlToImage.toJpeg : window.htmlToImage.toPng;
874
882
  var rasterOpts = {
875
883
  backgroundColor: bgColor,
876
- pixelRatio: format === "pdf" ? 2 : 4,
884
+ pixelRatio: 4,
877
885
  cacheBust: true,
878
886
  width: width,
879
887
  height: height,
880
888
  };
881
- if (format === "pdf") rasterOpts.quality = 0.92;
889
+ if (format === "pdf") rasterOpts.quality = 1.0;
882
890
  rasterFn(document.documentElement, rasterOpts).then(function(dataUrl){
883
891
  parent.postMessage({ type: "easel:image-ready", pushId: pushId, dataUrl: dataUrl, filename: filename, format: format }, "*");
884
892
  }).catch(function(err){
@@ -903,7 +911,7 @@ ${body}
903
911
  const configScript =
904
912
  "<script src='https://cdn.jsdelivr.net/npm/html-to-image@1.11.13/dist/html-to-image.js'></script><script>(function(){function a(c){if(!c)return;if(c.theme==='light'||c.theme==='dark'){document.documentElement.setAttribute('data-theme',c.theme);window.__claudeDisplayTheme=c.theme}if(c.preset==='paper'||c.preset==='aurora'||c.preset==='slate'){document.documentElement.setAttribute('data-preset',c.preset);window.__claudeDisplayPreset=c.preset}if(c.density==='carded'||c.density==='flat'){document.documentElement.setAttribute('data-density',c.density);window.__claudeDisplayDensity=c.density}}a(" +
905
913
  JSON.stringify({ theme, preset, density }) +
906
- ");window.addEventListener('message',function(e){if(!e||!e.data)return;if(e.data.type==='easel:config')a(e.data);if(e.data.type==='easel:theme')a({theme:e.data.theme});if(e.data.type==='easel:print'){try{window.print()}catch(_){}}if(e.data.type==='easel:image'){var pid=e.data.pushId;var fn=e.data.filename||'push.png';var fmt=e.data.format==='pdf'?'pdf':'png';var bg=e.data.bgColor||'#ffffff';if(!window.htmlToImage)return;var rfn=fmt==='pdf'?window.htmlToImage.toJpeg:window.htmlToImage.toPng;var ropts={backgroundColor:bg,pixelRatio:fmt==='pdf'?2:4,cacheBust:true};if(fmt==='pdf')ropts.quality=0.92;rfn(document.body,ropts).then(function(u){parent.postMessage({type:'easel:image-ready',pushId:pid,dataUrl:u,filename:fn,format:fmt},'*')}).catch(function(err){console.error(err);parent.postMessage({type:'easel:image-error',pushId:pid,format:fmt,message:(err&&err.message)?err.message:String(err)},'*')})}})})();</script>";
914
+ ");window.addEventListener('message',function(e){if(!e||!e.data)return;if(e.data.type==='easel:config')a(e.data);if(e.data.type==='easel:theme')a({theme:e.data.theme});if(e.data.type==='easel:print'){try{window.print()}catch(_){}}if(e.data.type==='easel:image'){var pid=e.data.pushId;var fn=e.data.filename||'push.png';var fmt=e.data.format==='pdf'?'pdf':'png';var bg=e.data.bgColor||'#ffffff';if(!window.htmlToImage)return;var rfn=fmt==='pdf'?window.htmlToImage.toJpeg:window.htmlToImage.toPng;var ropts={backgroundColor:bg,pixelRatio:4,cacheBust:true};if(fmt==='pdf')ropts.quality=1.0;rfn(document.body,ropts).then(function(u){parent.postMessage({type:'easel:image-ready',pushId:pid,dataUrl:u,filename:fn,format:fmt},'*')}).catch(function(err){console.error(err);parent.postMessage({type:'easel:image-error',pushId:pid,format:fmt,message:(err&&err.message)?err.message:String(err)},'*')})}})})();</script>";
907
915
  const measureScript = "<script>" + selfMeasureScript(pushId) + "</script>";
908
916
  const combined = configScript + measureScript;
909
917
  if (/<\/body>/i.test(html)) return html.replace(/<\/body>/i, combined + "</body>");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ammduncan/easel",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "A live browser tab for every Claude Code (and MCP) session. The push MCP tool appends HTML cards to a scrolling feed you keep open in split-screen.",
5
5
  "type": "module",
6
6
  "license": "MIT",