pi-studio 0.9.37 → 0.9.38

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
@@ -4,6 +4,11 @@ All notable changes to `pi-studio` are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.38] — 2026-08-01
8
+
9
+ ### Fixed
10
+ - Preserved corrected Mermaid node colours during Chromium PDF printing and gave external icon-node labels a theme-aware backdrop, preventing washed-out labels beside custom icon fills.
11
+
7
12
  ## [0.9.37] — 2026-08-01
8
13
 
9
14
  ### Added
package/index.ts CHANGED
@@ -5843,14 +5843,14 @@ async function renderStudioMermaidDiagramForPdf(source: string, workDir: string,
5843
5843
  const mermaidTheme = getStudioMermaidPdfTheme();
5844
5844
  const inputPath = join(workDir, `mermaid-diagram-${blockNumber}.mmd`);
5845
5845
  const outputPath = join(workDir, `mermaid-diagram-${blockNumber}.pdf`);
5846
- const iconCssPath = join(workDir, `mermaid-diagram-${blockNumber}.css`);
5846
+ const contrastCssPath = join(workDir, `mermaid-diagram-${blockNumber}.css`);
5847
5847
 
5848
5848
  const preparedSource = ensureStudioMermaidSourceContrast(source);
5849
- const iconContrastCss = buildStudioMermaidPdfIconContrastCss(preparedSource);
5849
+ const contrastCss = buildStudioMermaidPdfIconContrastCss(preparedSource, { theme: mermaidTheme });
5850
5850
  await writeFile(inputPath, preparedSource, "utf-8");
5851
- if (iconContrastCss) await writeFile(iconCssPath, iconContrastCss, "utf-8");
5851
+ if (contrastCss) await writeFile(contrastCssPath, contrastCss, "utf-8");
5852
5852
  const args = ["-i", inputPath, "-o", outputPath, "-t", mermaidTheme, "-f"];
5853
- if (iconContrastCss) args.push("-C", iconCssPath);
5853
+ if (contrastCss) args.push("-C", contrastCssPath);
5854
5854
  args.push(...buildStudioMermaidCliIconArgs(preparedSource));
5855
5855
  const result = await runStudioSubprocess(mermaidCommand, args, {
5856
5856
  timeoutMs: STUDIO_MERMAID_TIMEOUT_MS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.9.37",
3
+ "version": "0.9.38",
4
4
  "description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, active quiz, prompt/response history, live previews, and tmux-backed REPL/literate REPL workflows",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -232,12 +232,44 @@ function buildStudioMermaidIconPaintRule(selector, color) {
232
232
  ].join("\n");
233
233
  }
234
234
 
235
- export function buildStudioMermaidPdfIconContrastCss(source) {
235
+ function buildStudioMermaidPdfPrintColorRule() {
236
+ return [
237
+ ".node, .node *, .icon-shape, .icon-shape * {",
238
+ " -webkit-print-color-adjust: exact !important;",
239
+ " print-color-adjust: exact !important;",
240
+ "}",
241
+ ].join("\n");
242
+ }
243
+
244
+ function buildStudioMermaidPdfIconLabelRule(selectors, theme) {
245
+ if (selectors.length === 0) return "";
246
+ const darkTheme = String(theme || "default").toLowerCase() === "dark";
247
+ const background = darkTheme ? "#1f2937" : "#ffffff";
248
+ const foreground = darkTheme ? "#ffffff" : "#000000";
249
+ const backgroundSelectors = selectors.map((selector) => `${selector} .labelBkg`).join(",\n");
250
+ const textSelectors = selectors.flatMap((selector) => [
251
+ `${selector} .nodeLabel`,
252
+ `${selector} .nodeLabel *`,
253
+ ]).join(",\n");
254
+ return [
255
+ `${backgroundSelectors} { background-color: ${background} !important; }`,
256
+ `${textSelectors} {`,
257
+ ` color: ${foreground} !important;`,
258
+ ` fill: ${foreground} !important;`,
259
+ ` -webkit-text-fill-color: ${foreground} !important;`,
260
+ " opacity: 1 !important;",
261
+ "}",
262
+ ].join("\n");
263
+ }
264
+
265
+ export function buildStudioMermaidPdfIconContrastCss(source, options = {}) {
236
266
  const preparedSource = ensureStudioMermaidSourceContrast(source);
237
267
  const { iconNodeIds, classNamesByNode } = collectStudioMermaidIconStyleTargets(preparedSource);
238
- if (iconNodeIds.size === 0) return "";
268
+ const printColorRule = buildStudioMermaidPdfPrintColorRule();
269
+ if (iconNodeIds.size === 0) return printColorRule;
239
270
  const classStyles = collectStudioMermaidStylesByTarget(preparedSource, "classDef");
240
271
  const directStyles = collectStudioMermaidStylesByTarget(preparedSource, "style");
272
+ const iconSelectors = Array.from(iconNodeIds, (iconNodeId) => `.icon-shape[id*="flowchart-${iconNodeId}-"]`);
241
273
  const rulesBySelector = new Map();
242
274
 
243
275
  for (const iconNodeId of iconNodeIds) {
@@ -250,5 +282,9 @@ export function buildStudioMermaidPdfIconContrastCss(source) {
250
282
  if (paint) rulesBySelector.set(`.icon-shape[id*="flowchart-${iconNodeId}-"]`, paint);
251
283
  }
252
284
 
253
- return Array.from(rulesBySelector, ([selector, color]) => buildStudioMermaidIconPaintRule(selector, color)).join("\n");
285
+ return [
286
+ printColorRule,
287
+ buildStudioMermaidPdfIconLabelRule(iconSelectors, options.theme),
288
+ ...Array.from(rulesBySelector, ([selector, color]) => buildStudioMermaidIconPaintRule(selector, color)),
289
+ ].filter(Boolean).join("\n");
254
290
  }