@oh-my-pi/snapcompact 16.3.6 → 16.3.8

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.3.7] - 2026-07-05
6
+
7
+ ### Fixed
8
+
9
+ - Fixed `resolveShapeForText(..., "auto")` to correctly select the `silver16-bw` shape for CJK-heavy transcript text while preserving explicit shape overrides.
10
+
5
11
  ## [16.2.8] - 2026-06-30
6
12
 
7
13
  ### Fixed
@@ -1 +1 @@
1
- export * from "./snapcompact";
1
+ export * from "./snapcompact.js";
@@ -290,13 +290,12 @@ export interface ShapeTarget {
290
290
  */
291
291
  export declare function resolveShape(model?: ShapeTarget, variant?: ShapeVariantName | "auto"): Shape;
292
292
  /**
293
- * Pick the frame shape for `text` without changing the selected shape.
294
- *
295
- * Glyph-level Silver fallback happens during normalization/rendering, so this
296
- * helper exists for callers that need a text-aware API name while preserving
297
- * explicit and provider-selected shapes.
293
+ * Pick the frame shape for `text`. Explicit variants remain forced. Auto first
294
+ * resolves the model/provider default, then selects the Silver CJK grid when
295
+ * the default font cannot safely render the text or wide CJK glyphs dominate
296
+ * the transcript and Silver can render it safely.
298
297
  */
299
- export declare function resolveShapeForText(_text: string, model?: ShapeTarget, variant?: ShapeVariantName | "auto"): Shape;
298
+ export declare function resolveShapeForText(text: string, model?: ShapeTarget, variant?: ShapeVariantName | "auto"): Shape;
300
299
  /** Legacy frame edge in pixels (the 5x8 shape's eval-validated size). New
301
300
  * shapes carry their own `frameSize`. */
302
301
  export declare const FRAME_SIZE = 2576;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/snapcompact",
4
- "version": "16.3.6",
4
+ "version": "16.3.8",
5
5
  "description": "Bitmap-frame context compression for vision-capable LLMs",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -31,10 +31,10 @@
31
31
  "fmt": "biome format --write ."
32
32
  },
33
33
  "dependencies": {
34
- "@oh-my-pi/pi-ai": "16.3.6",
35
- "@oh-my-pi/pi-natives": "16.3.6",
36
- "@oh-my-pi/pi-utils": "16.3.6",
37
- "@oh-my-pi/pi-wire": "16.3.6"
34
+ "@oh-my-pi/pi-ai": "16.3.8",
35
+ "@oh-my-pi/pi-natives": "16.3.8",
36
+ "@oh-my-pi/pi-utils": "16.3.8",
37
+ "@oh-my-pi/pi-wire": "16.3.8"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/bun": "^1.3.14"
@@ -385,15 +385,39 @@ export function resolveShape(model?: ShapeTarget, variant?: ShapeVariantName | "
385
385
  return priceShape(ideal?.frameSize ? { ...base, frameSize: ideal.frameSize } : base, family);
386
386
  }
387
387
 
388
+ const CJK_HEAVY_MIN_WIDE_CHARS = 8;
389
+ const CJK_HEAVY_WIDE_RATIO = 0.25;
390
+
391
+ function isCjkHeavyText(text: string): boolean {
392
+ const chars = normalizedInputChars(text);
393
+ let graphicChars = 0;
394
+ let wideChars = 0;
395
+ for (const ch of chars) {
396
+ if (ch === " " || ch === DIM_ON || ch === DIM_OFF || ch === NEWLINE_GLYPH) continue;
397
+ const cp = ch.codePointAt(0);
398
+ if (cp === undefined || UNRENDERABLE.test(ch)) continue;
399
+ graphicChars++;
400
+ if (isWideCodePoint(cp)) wideChars++;
401
+ }
402
+ return wideChars >= CJK_HEAVY_MIN_WIDE_CHARS && wideChars / graphicChars >= CJK_HEAVY_WIDE_RATIO;
403
+ }
404
+
388
405
  /**
389
- * Pick the frame shape for `text` without changing the selected shape.
390
- *
391
- * Glyph-level Silver fallback happens during normalization/rendering, so this
392
- * helper exists for callers that need a text-aware API name while preserving
393
- * explicit and provider-selected shapes.
406
+ * Pick the frame shape for `text`. Explicit variants remain forced. Auto first
407
+ * resolves the model/provider default, then selects the Silver CJK grid when
408
+ * the default font cannot safely render the text or wide CJK glyphs dominate
409
+ * the transcript and Silver can render it safely.
394
410
  */
395
- export function resolveShapeForText(_text: string, model?: ShapeTarget, variant?: ShapeVariantName | "auto"): Shape {
396
- return resolveShape(model, variant);
411
+ export function resolveShapeForText(text: string, model?: ShapeTarget, variant?: ShapeVariantName | "auto"): Shape {
412
+ const shape = resolveShape(model, variant);
413
+ if (variant && variant !== "auto") return shape;
414
+ const silver = resolveShape(model, "silver16-bw");
415
+ if (!scanRenderability(text, { shape }).isSafe) {
416
+ return scanRenderability(text, { shape: silver }).isSafe ? silver : shape;
417
+ }
418
+ return shape.font !== "silver" && isCjkHeavyText(text) && scanRenderability(text, { shape: silver }).isSafe
419
+ ? silver
420
+ : shape;
397
421
  }
398
422
 
399
423
  // ============================================================================