restty 0.1.20 → 0.1.21
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/README.md +1 -0
- package/dist/{chunk-ef12eja6.js → chunk-ym658zhj.js} +104 -10
- package/dist/fonts/manager.d.ts +1 -1
- package/dist/internal.js +1 -1
- package/dist/restty.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
[](https://github.com/wiedymi/restty/actions/workflows/ci.yml)
|
|
6
6
|
[](https://github.com/wiedymi/restty/actions/workflows/publish.yml)
|
|
7
7
|
[](https://restty.pages.dev/)
|
|
8
|
+
|
|
8
9
|
[](https://github.com/wiedymi)
|
|
9
10
|
[](https://x.com/wiedymi)
|
|
10
11
|
[](mailto:contact@wiedymi.com)
|
|
@@ -9095,6 +9095,37 @@ function isLikelyEmojiCodepoint(cp) {
|
|
|
9095
9095
|
return true;
|
|
9096
9096
|
return false;
|
|
9097
9097
|
}
|
|
9098
|
+
function isVariationSelectorCodepoint(cp) {
|
|
9099
|
+
if (cp >= 65024 && cp <= 65039)
|
|
9100
|
+
return true;
|
|
9101
|
+
if (cp >= 917760 && cp <= 917999)
|
|
9102
|
+
return true;
|
|
9103
|
+
return false;
|
|
9104
|
+
}
|
|
9105
|
+
function isCombiningMarkCodepoint(cp) {
|
|
9106
|
+
if (cp >= 768 && cp <= 879)
|
|
9107
|
+
return true;
|
|
9108
|
+
if (cp >= 6832 && cp <= 6911)
|
|
9109
|
+
return true;
|
|
9110
|
+
if (cp >= 7616 && cp <= 7679)
|
|
9111
|
+
return true;
|
|
9112
|
+
if (cp >= 8400 && cp <= 8447)
|
|
9113
|
+
return true;
|
|
9114
|
+
if (cp >= 65056 && cp <= 65071)
|
|
9115
|
+
return true;
|
|
9116
|
+
return false;
|
|
9117
|
+
}
|
|
9118
|
+
function isCoverageIgnorableCodepoint(cp) {
|
|
9119
|
+
if (cp === 8204 || cp === 8205)
|
|
9120
|
+
return true;
|
|
9121
|
+
if (isVariationSelectorCodepoint(cp))
|
|
9122
|
+
return true;
|
|
9123
|
+
if (isCombiningMarkCodepoint(cp))
|
|
9124
|
+
return true;
|
|
9125
|
+
if (cp >= 917536 && cp <= 917631)
|
|
9126
|
+
return true;
|
|
9127
|
+
return false;
|
|
9128
|
+
}
|
|
9098
9129
|
function resolvePresentationPreference(text, chars) {
|
|
9099
9130
|
if (text.includes("️"))
|
|
9100
9131
|
return "emoji";
|
|
@@ -9109,7 +9140,7 @@ function resolvePresentationPreference(text, chars) {
|
|
|
9109
9140
|
}
|
|
9110
9141
|
return "auto";
|
|
9111
9142
|
}
|
|
9112
|
-
function pickFontIndexForText(state, text, expectedSpan
|
|
9143
|
+
function pickFontIndexForText(state, text, expectedSpan) {
|
|
9113
9144
|
if (!state.fonts.length)
|
|
9114
9145
|
return 0;
|
|
9115
9146
|
const cacheKey = `${expectedSpan}:${text}`;
|
|
@@ -9117,6 +9148,10 @@ function pickFontIndexForText(state, text, expectedSpan, shapeClusterWithFont) {
|
|
|
9117
9148
|
if (cached !== undefined)
|
|
9118
9149
|
return cached;
|
|
9119
9150
|
const chars = Array.from(text);
|
|
9151
|
+
const requiredChars = chars.filter((ch) => {
|
|
9152
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
9153
|
+
return !isCoverageIgnorableCodepoint(cp);
|
|
9154
|
+
});
|
|
9120
9155
|
const firstCp = text.codePointAt(0) ?? 0;
|
|
9121
9156
|
const nerdSymbol = isNerdSymbolCodepoint(firstCp);
|
|
9122
9157
|
const presentation = resolvePresentationPreference(text, chars);
|
|
@@ -9128,7 +9163,7 @@ function pickFontIndexForText(state, text, expectedSpan, shapeClusterWithFont) {
|
|
|
9128
9163
|
if (predicate && !predicate(entry))
|
|
9129
9164
|
continue;
|
|
9130
9165
|
let ok = true;
|
|
9131
|
-
for (const ch of
|
|
9166
|
+
for (const ch of requiredChars) {
|
|
9132
9167
|
if (!fontHasGlyph(entry.font, ch)) {
|
|
9133
9168
|
ok = false;
|
|
9134
9169
|
break;
|
|
@@ -53715,6 +53750,51 @@ function createResttyApp(options) {
|
|
|
53715
53750
|
return true;
|
|
53716
53751
|
return false;
|
|
53717
53752
|
}
|
|
53753
|
+
function isVariationSelectorCodepoint2(cp) {
|
|
53754
|
+
if (cp >= 65024 && cp <= 65039)
|
|
53755
|
+
return true;
|
|
53756
|
+
if (cp >= 917760 && cp <= 917999)
|
|
53757
|
+
return true;
|
|
53758
|
+
return false;
|
|
53759
|
+
}
|
|
53760
|
+
function isCombiningMarkCodepoint2(cp) {
|
|
53761
|
+
if (cp >= 768 && cp <= 879)
|
|
53762
|
+
return true;
|
|
53763
|
+
if (cp >= 6832 && cp <= 6911)
|
|
53764
|
+
return true;
|
|
53765
|
+
if (cp >= 7616 && cp <= 7679)
|
|
53766
|
+
return true;
|
|
53767
|
+
if (cp >= 8400 && cp <= 8447)
|
|
53768
|
+
return true;
|
|
53769
|
+
if (cp >= 65056 && cp <= 65071)
|
|
53770
|
+
return true;
|
|
53771
|
+
return false;
|
|
53772
|
+
}
|
|
53773
|
+
function isEmojiModifierCodepoint(cp) {
|
|
53774
|
+
return cp >= 127995 && cp <= 127999;
|
|
53775
|
+
}
|
|
53776
|
+
function isCoverageIgnorableCodepoint2(cp) {
|
|
53777
|
+
if (cp === 8204 || cp === 8205)
|
|
53778
|
+
return true;
|
|
53779
|
+
if (isVariationSelectorCodepoint2(cp))
|
|
53780
|
+
return true;
|
|
53781
|
+
if (isCombiningMarkCodepoint2(cp))
|
|
53782
|
+
return true;
|
|
53783
|
+
if (cp >= 917536 && cp <= 917631)
|
|
53784
|
+
return true;
|
|
53785
|
+
return false;
|
|
53786
|
+
}
|
|
53787
|
+
function shouldMergeTrailingClusterCodepoint(cp) {
|
|
53788
|
+
if (cp === 8204 || cp === 8205)
|
|
53789
|
+
return true;
|
|
53790
|
+
if (isVariationSelectorCodepoint2(cp))
|
|
53791
|
+
return true;
|
|
53792
|
+
if (isCombiningMarkCodepoint2(cp))
|
|
53793
|
+
return true;
|
|
53794
|
+
if (isEmojiModifierCodepoint(cp))
|
|
53795
|
+
return true;
|
|
53796
|
+
return false;
|
|
53797
|
+
}
|
|
53718
53798
|
function resolvePresentationPreference2(text, chars) {
|
|
53719
53799
|
if (text.includes("️"))
|
|
53720
53800
|
return "emoji";
|
|
@@ -53737,6 +53817,10 @@ function createResttyApp(options) {
|
|
|
53737
53817
|
if (cached !== undefined)
|
|
53738
53818
|
return cached;
|
|
53739
53819
|
const chars = Array.from(text);
|
|
53820
|
+
const requiredChars = chars.filter((ch) => {
|
|
53821
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
53822
|
+
return !isCoverageIgnorableCodepoint2(cp);
|
|
53823
|
+
});
|
|
53740
53824
|
const firstCp = text.codePointAt(0) ?? 0;
|
|
53741
53825
|
const nerdSymbol = isNerdSymbolCodepoint(firstCp);
|
|
53742
53826
|
const presentation = resolvePresentationPreference2(text, chars);
|
|
@@ -53751,7 +53835,7 @@ function createResttyApp(options) {
|
|
|
53751
53835
|
(entry) => hasItalicHint(entry) && !hasBoldHint(entry),
|
|
53752
53836
|
(entry) => hasItalicHint(entry)
|
|
53753
53837
|
] : [];
|
|
53754
|
-
const pickFirstMatch = (predicate) => {
|
|
53838
|
+
const pickFirstMatch = (predicate, allowSequenceShapingFallback = false) => {
|
|
53755
53839
|
for (let i3 = 0;i3 < fontState.fonts.length; i3 += 1) {
|
|
53756
53840
|
const entry = fontState.fonts[i3];
|
|
53757
53841
|
if (!entry?.font)
|
|
@@ -53759,18 +53843,22 @@ function createResttyApp(options) {
|
|
|
53759
53843
|
if (predicate && !predicate(entry))
|
|
53760
53844
|
continue;
|
|
53761
53845
|
let ok = true;
|
|
53762
|
-
for (const ch of
|
|
53846
|
+
for (const ch of requiredChars) {
|
|
53763
53847
|
if (!fontHasGlyph2(entry.font, ch)) {
|
|
53764
53848
|
ok = false;
|
|
53765
53849
|
break;
|
|
53766
53850
|
}
|
|
53767
53851
|
}
|
|
53852
|
+
if (!ok && allowSequenceShapingFallback) {
|
|
53853
|
+
const shaped = shapeClusterWithFont(entry, text);
|
|
53854
|
+
ok = shaped.glyphs.some((glyph) => (glyph.glyphId ?? 0) !== 0);
|
|
53855
|
+
}
|
|
53768
53856
|
if (ok)
|
|
53769
53857
|
return i3;
|
|
53770
53858
|
}
|
|
53771
53859
|
return -1;
|
|
53772
53860
|
};
|
|
53773
|
-
const pickWithStyle = (predicate) => {
|
|
53861
|
+
const pickWithStyle = (predicate, allowSequenceShapingFallback = false) => {
|
|
53774
53862
|
if (styleHintsEnabled) {
|
|
53775
53863
|
for (let i3 = 0;i3 < stylePredicates.length; i3 += 1) {
|
|
53776
53864
|
const stylePredicate = stylePredicates[i3];
|
|
@@ -53778,12 +53866,12 @@ function createResttyApp(options) {
|
|
|
53778
53866
|
if (!stylePredicate(entry))
|
|
53779
53867
|
return false;
|
|
53780
53868
|
return predicate ? !!predicate(entry) : true;
|
|
53781
|
-
});
|
|
53869
|
+
}, allowSequenceShapingFallback);
|
|
53782
53870
|
if (styledIndex >= 0)
|
|
53783
53871
|
return styledIndex;
|
|
53784
53872
|
}
|
|
53785
53873
|
}
|
|
53786
|
-
return pickFirstMatch(predicate);
|
|
53874
|
+
return pickFirstMatch(predicate, allowSequenceShapingFallback);
|
|
53787
53875
|
};
|
|
53788
53876
|
const tryIndex = (index) => {
|
|
53789
53877
|
if (index < 0)
|
|
@@ -53798,7 +53886,7 @@ function createResttyApp(options) {
|
|
|
53798
53886
|
return result;
|
|
53799
53887
|
}
|
|
53800
53888
|
if (presentation === "emoji") {
|
|
53801
|
-
const emojiIndex = pickFirstMatch((entry) => isColorEmojiFont(entry));
|
|
53889
|
+
const emojiIndex = pickFirstMatch((entry) => isColorEmojiFont(entry), true);
|
|
53802
53890
|
const result = tryIndex(emojiIndex);
|
|
53803
53891
|
if (result !== null)
|
|
53804
53892
|
return result;
|
|
@@ -54508,10 +54596,13 @@ function createResttyApp(options) {
|
|
|
54508
54596
|
}
|
|
54509
54597
|
let nextSeqIdx = idx + baseSpan;
|
|
54510
54598
|
let guard = 0;
|
|
54511
|
-
while (
|
|
54599
|
+
while (nextSeqIdx < rowEnd && guard < 12) {
|
|
54512
54600
|
const next = readCellCluster(nextSeqIdx);
|
|
54513
54601
|
if (!next || !next.cp || isSpaceCp(next.cp))
|
|
54514
54602
|
break;
|
|
54603
|
+
const shouldMerge = text.endsWith("") || shouldMergeTrailingClusterCodepoint(next.cp);
|
|
54604
|
+
if (!shouldMerge)
|
|
54605
|
+
break;
|
|
54515
54606
|
text += next.text;
|
|
54516
54607
|
baseSpan += next.span;
|
|
54517
54608
|
mergedEmojiSkip[nextSeqIdx] = 1;
|
|
@@ -55374,10 +55465,13 @@ function createResttyApp(options) {
|
|
|
55374
55465
|
}
|
|
55375
55466
|
let nextSeqIdx = idx + baseSpan;
|
|
55376
55467
|
let guard = 0;
|
|
55377
|
-
while (
|
|
55468
|
+
while (nextSeqIdx < rowEnd && guard < 12) {
|
|
55378
55469
|
const next = readCellCluster(nextSeqIdx);
|
|
55379
55470
|
if (!next || !next.cp || isSpaceCp(next.cp))
|
|
55380
55471
|
break;
|
|
55472
|
+
const shouldMerge = text.endsWith("") || shouldMergeTrailingClusterCodepoint(next.cp);
|
|
55473
|
+
if (!shouldMerge)
|
|
55474
|
+
break;
|
|
55381
55475
|
text += next.text;
|
|
55382
55476
|
baseSpan += next.span;
|
|
55383
55477
|
mergedEmojiSkip[nextSeqIdx] = 1;
|
package/dist/fonts/manager.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare function glyphWidthUnits(entry: FontEntry, glyphId: number | unde
|
|
|
27
27
|
* Select the best font index from the manager's font list for rendering the
|
|
28
28
|
* given text cluster, searching in fallback order similar to Ghostty.
|
|
29
29
|
*/
|
|
30
|
-
export declare function pickFontIndexForText(state: FontManagerState, text: string, expectedSpan: number
|
|
30
|
+
export declare function pickFontIndexForText(state: FontManagerState, text: string, expectedSpan: number): number;
|
|
31
31
|
/** Fetch a font file from a URL and return its ArrayBuffer, or null on failure. */
|
|
32
32
|
export declare function tryFetchFontBuffer(url: string): Promise<ArrayBuffer | null>;
|
|
33
33
|
/** Query locally installed fonts via the Local Font Access API and return the first match, or null. */
|
package/dist/internal.js
CHANGED
package/dist/restty.js
CHANGED