pi-claude-code-ui 1.0.85 → 1.0.86

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
@@ -3,6 +3,12 @@
3
3
  > [!IMPORTANT]
4
4
  > **1.0.69 — package rename (permanent).** Canonical npm name is now [`pi-claude-code-ui`](https://www.npmjs.com/package/pi-claude-code-ui). `pi-claude-style-tools` is legacy and will not receive further releases. Install with `pi install npm:pi-claude-code-ui` or `npm i pi-claude-code-ui`.
5
5
 
6
+ ## 1.0.86 — 2026-10-08
7
+
8
+ ### Fixed
9
+
10
+ - **Display-math scanning no longer splits fenced blocks on `$$` and `\[ … \]`** (Perry Trolard, [#33](https://github.com/FammasMaz/pi-cc-tools/issues/33)) - paragraph rebuilding now respects the same Markdown code regions as inline math formatting, so `$$` (shell PID) and `\[ … \]` (grep/ripgrep char-class regexes) inside ```` ``` ```` fences and inline code stay literal instead of cutting the block into a math segment.
11
+
6
12
  ## 1.0.83 — 2026-09-07
7
13
 
8
14
  ### Fixed
@@ -2001,6 +2001,32 @@ function patchTerminalWriteTagScrubber(): void {
2001
2001
  proto[TERMINAL_SCRUB_PATCH_FLAG] = true;
2002
2002
  }
2003
2003
 
2004
+ // Code spans and fenced blocks are literal Markdown source. Math preprocessing
2005
+ // must never touch delimiters inside them: `$$` is the shell PID and `\[ … \]
2006
+ // appears in grep/ripgrep class regexes. Inline formatting splits on this
2007
+ // expression before transforming; display-math block scanning below reuses it
2008
+ // so a math candidate opening inside code is skipped in favor of real prose.
2009
+ const MARKDOWN_CODE_REGION_RE = /(`{3,}[\s\S]*?`{3,}|`[^`\n]*`)/g;
2010
+
2011
+ interface CodeRegion {
2012
+ start: number;
2013
+ end: number;
2014
+ }
2015
+
2016
+ function findMarkdownCodeRegions(text: string): CodeRegion[] {
2017
+ const regions: CodeRegion[] = [];
2018
+ MARKDOWN_CODE_REGION_RE.lastIndex = 0;
2019
+ let match: RegExpExecArray | null;
2020
+ while ((match = MARKDOWN_CODE_REGION_RE.exec(text))) {
2021
+ regions.push({ start: match.index, end: match.index + match[0].length });
2022
+ }
2023
+ return regions;
2024
+ }
2025
+
2026
+ function codeRegionContaining(regions: CodeRegion[], index: number): CodeRegion | undefined {
2027
+ return regions.find((region) => index >= region.start && index < region.end);
2028
+ }
2029
+
2004
2030
  function replaceInlineMath(text: string): string {
2005
2031
  if (!hasInlineMathMarkers(text)) return text;
2006
2032
  const transform = (segment: string): string => {
@@ -2012,10 +2038,7 @@ function replaceInlineMath(text: string): string {
2012
2038
  return `${prefix}${codeSpan(formatMathForDisplay(body, false))}`;
2013
2039
  });
2014
2040
  };
2015
- // Markdown code spans and fenced blocks are literal source. Apply math
2016
- // formatting only to prose segments so shell variables such as `$base` and
2017
- // `$scope` remain byte-for-byte unchanged in displayed/copied commands.
2018
- const parts = text.split(/(`{3,}[\s\S]*?`{3,}|`[^`\n]*`)/g);
2041
+ const parts = text.split(MARKDOWN_CODE_REGION_RE);
2019
2042
  return parts.map((part, index) => index % 2 === 1 ? part : transform(part)).join("");
2020
2043
  }
2021
2044
 
@@ -2132,14 +2155,31 @@ function buildParagraphSegments(
2132
2155
  return segments;
2133
2156
  }
2134
2157
  const scanLoose = shouldScanLooseBracketMath(text);
2158
+ // Code regions are computed once for the whole paragraph: a display-math
2159
+ // candidate must be rejected before its fence gets cut into separate
2160
+ // Markdown segments (issue #33, sibling of the inline fix in 2ccd158).
2161
+ const codeRegions = findMarkdownCodeRegions(text);
2162
+ // `cursor` marks the start of prose not yet emitted; `scanFrom` only advances
2163
+ // past rejected code regions so skipped candidates never erase prose.
2135
2164
  let cursor = 0;
2136
- while (cursor < text.length) {
2137
- const next = findNextDisplayMathBlock(text, cursor, scanLoose);
2165
+ let scanFrom = 0;
2166
+ while (scanFrom < text.length) {
2167
+ let next = findNextDisplayMathBlock(text, scanFrom, scanLoose);
2168
+ // Math delimiters found inside a fenced block or code span are literal
2169
+ // source, not math. Resume the scan after that code region so a later
2170
+ // genuine `$$ … $$` in prose is still honored.
2171
+ while (next) {
2172
+ const region = codeRegionContaining(codeRegions, next.index);
2173
+ if (!region) break;
2174
+ scanFrom = region.end;
2175
+ next = findNextDisplayMathBlock(text, scanFrom, scanLoose);
2176
+ }
2138
2177
  if (!next) break;
2139
2178
  appendMarkdownSegment(segments, text.slice(cursor, next.index), theme, options);
2140
2179
  const raw = text.slice(next.contentStart, next.contentEnd).trim();
2141
2180
  if (raw) segments.push({ kind: "math", raw });
2142
2181
  cursor = next.endIndex;
2182
+ scanFrom = next.endIndex;
2143
2183
  }
2144
2184
  appendMarkdownSegment(segments, text.slice(cursor), theme, options);
2145
2185
  return segments;
@@ -7876,3 +7916,11 @@ export default function (pi: ExtensionAPI) {
7876
7916
  bumpToolBranchVisualEpoch();
7877
7917
  });
7878
7918
  }
7919
+
7920
+ // Test-only escape hatch: this package is a default-export extension and the
7921
+ // export surface Pi consumes must stay minimal. Regression tests for math
7922
+ // preprocessing reach these internals through this namespace instead of
7923
+ // widening the public export (see scripts/math-preprocess.test.ts).
7924
+ export const __mathPreprocessInternals = {
7925
+ buildParagraphSegments,
7926
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-claude-code-ui",
3
- "version": "1.0.85",
3
+ "version": "1.0.86",
4
4
  "description": "Claude Code-style grouped tool rows for pi with Ctrl+O/Ctrl+Shift+O previews, math rendering, and consistent built-in, MCP, and custom tool rendering",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -37,7 +37,7 @@
37
37
  "CHANGELOG.md"
38
38
  ],
39
39
  "scripts": {
40
- "test": "node --experimental-strip-types --test scripts/*.test.ts",
40
+ "test": "node --experimental-strip-types --test scripts/*.test.ts scripts/*.test.mts",
41
41
  "test:fullscreen": "node scripts/fullscreen-mouse.test.mjs",
42
42
  "typecheck": "tsc --noEmit --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck extensions/index.ts extensions/spinner.ts",
43
43
  "pack:dry-run": "npm pack --dry-run",