@kurot/core 2.2.0 → 2.2.1
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 +31 -0
- package/README.md +21 -8
- package/dist/kurot/text/LineBreaks.d.ts +2 -1
- package/dist/kurot/text/LineBreaks.d.ts.map +1 -1
- package/dist/kurot/text/LineBreaks.js +13 -9
- package/dist/kurot/text/LineBreaks.js.map +1 -1
- package/dist/kurot/text/TextLineLayout.d.ts.map +1 -1
- package/dist/kurot/text/TextLineLayout.js +5 -12
- package/dist/kurot/text/TextLineLayout.js.map +1 -1
- package/dist/kurot/text/TextSegmentation.d.ts +7 -0
- package/dist/kurot/text/TextSegmentation.d.ts.map +1 -0
- package/dist/kurot/text/TextSegmentation.js +17 -0
- package/dist/kurot/text/TextSegmentation.js.map +1 -0
- package/dist/kurot/text/WordWrap.d.ts.map +1 -1
- package/dist/kurot/text/WordWrap.js +3 -11
- package/dist/kurot/text/WordWrap.js.map +1 -1
- package/docs/ai-context.md +13 -4
- package/docs/architecture.md +9 -2
- package/docs/text-layout.md +69 -8
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ All notable changes to `@kurot/core` are documented here.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [2.2.1] — 2026-10-08
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Keep Thai/Lao/Khmer dictionary wrapping and alphabetic overflow wrapping
|
|
12
|
+
functional in minified bundles that rename rule functions. Customize LB28 by
|
|
13
|
+
its imported function identity instead of its name.
|
|
14
|
+
- Protect grapheme clusters in normal word wrapping, emergency word wrapping
|
|
15
|
+
and `wordWrap = false`. Family emoji, flags, skin-tone modifiers, combining
|
|
16
|
+
marks and conjuncts stay intact. An oversized cluster can overflow the field
|
|
17
|
+
width rather than being split.
|
|
18
|
+
|
|
19
|
+
### Internal
|
|
20
|
+
|
|
21
|
+
- Share cached word/grapheme `Intl.Segmenter` instances with the public
|
|
22
|
+
segmentation utilities. Reuse each paragraph's grapheme end offsets between
|
|
23
|
+
ordinary and emergency layout; do not retain text or segment results globally.
|
|
24
|
+
- Keep `@cto.af/linebreak` pinned to 4.0.3 and the default Unicode 17.0 profile
|
|
25
|
+
unchanged. No public API, runtime dependency, resource or KUI format change.
|
|
26
|
+
|
|
27
|
+
### Tests
|
|
28
|
+
|
|
29
|
+
- Add a minified browser-bundle regression with `keepNames: false`, using
|
|
30
|
+
esbuild as a development dependency only.
|
|
31
|
+
- Cover complete-character wrapping, oversized clusters, rich-text boundaries,
|
|
32
|
+
CRLF source lengths, input hit testing and existing soft-wrap caret affinity.
|
|
33
|
+
- Retain all 19,338 official Unicode 17.0 line-break conformance cases.
|
|
34
|
+
- Verify a packed consumer with published UI 3.2.0 and DragonBones 0.1.0:
|
|
35
|
+
strict declaration checking and a complete minified bundle render correctly
|
|
36
|
+
through Canvas 2D, WebGL 1 and WebGL 2 with `keepNames: false`.
|
|
37
|
+
|
|
7
38
|
## [2.2.0] — 2026-10-08
|
|
8
39
|
|
|
9
40
|
### Added
|
package/README.md
CHANGED
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
A modern rewrite of the Egret game engine. Maintains Egret-compatible display object and event APIs while upgrading the rendering architecture, type safety, and tooling.
|
|
4
4
|
|
|
5
|
-
> **Package version: 2.2.
|
|
5
|
+
> **Package version: 2.2.1.** Targets ES2022 and evergreen browsers (Chrome / Edge / Firefox / Safari). No IE / old-Android / pre-2022 Safari support shims.
|
|
6
|
+
|
|
7
|
+
## Text wrapping fixes in 2.2.1
|
|
8
|
+
|
|
9
|
+
TextField wrapping remains functional when a bundler renames functions.
|
|
10
|
+
Dictionary wrapping and alphabetic overflow no longer depend on Unicode rule
|
|
11
|
+
function names. Both word wrapping and `wordWrap = false` keep grapheme
|
|
12
|
+
clusters intact, including family emoji, flags, skin-tone modifiers and combining
|
|
13
|
+
marks. A cluster wider than the field can overflow intact. Source and input
|
|
14
|
+
indices continue to use UTF-16 offsets.
|
|
15
|
+
|
|
16
|
+
The patch adds no public API, runtime dependency or resource/KUI format change.
|
|
17
|
+
The existing UI 3.2.0, Game 2.0.0, ui-runtime 0.8.2 and DragonBones 0.1.0 peer
|
|
18
|
+
ranges accept it. Update the installed Core version and lockfile to receive
|
|
19
|
+
the fixes; see [text layout](docs/text-layout.md).
|
|
6
20
|
|
|
7
21
|
## Scroll clipping in 2.1.1
|
|
8
22
|
|
|
@@ -34,15 +48,14 @@ matching UI release; Core does not expose `textFit` or `minFontSize`.
|
|
|
34
48
|
## Installation
|
|
35
49
|
|
|
36
50
|
```bash
|
|
37
|
-
pnpm add @kurot/core@^2.2.
|
|
51
|
+
pnpm add @kurot/core@^2.2.1
|
|
38
52
|
```
|
|
39
53
|
|
|
40
|
-
Core
|
|
41
|
-
layout. Package installation resolves this dependency from npm.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
it is absent from UI 3.1.0.
|
|
54
|
+
Core depends on `@kurot/bitmap-font ^0.1.0` for bitmap-font data and shared
|
|
55
|
+
layout. Package installation resolves this dependency from npm. Install other
|
|
56
|
+
Kurot packages separately when the application needs their capabilities.
|
|
57
|
+
Published UI 3.2.0 provides BitmapLabel and requires Core `^2.2.0`;
|
|
58
|
+
UI 3.1.0 does not contain this component.
|
|
46
59
|
|
|
47
60
|
## Features
|
|
48
61
|
|
|
@@ -12,6 +12,7 @@ export declare function getDefaultLineBreaks(text: string): LineBreakOpportunity
|
|
|
12
12
|
* Dictionary boundaries tailor complex-context scripts (Thai, Lao, Khmer).
|
|
13
13
|
* Emergency wrapping relaxes alphabetic word protection only; punctuation,
|
|
14
14
|
* non-breaking spaces and word joiners retain their Unicode constraints.
|
|
15
|
+
* Layout may reuse a paragraph's UTF-16 grapheme ends across both profiles.
|
|
15
16
|
*/
|
|
16
|
-
export declare function getWordLineBreaks(text: string, emergency?: boolean): LineBreakOpportunity[];
|
|
17
|
+
export declare function getWordLineBreaks(text: string, emergency?: boolean, graphemeEnds?: readonly number[]): LineBreakOpportunity[];
|
|
17
18
|
//# sourceMappingURL=LineBreaks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LineBreaks.d.ts","sourceRoot":"","sources":["../../../src/kurot/text/LineBreaks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LineBreaks.d.ts","sourceRoot":"","sources":["../../../src/kurot/text/LineBreaks.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CAClB;AAMD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAEzE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,EACZ,SAAS,UAAQ,EACjB,YAAY,GAAE,SAAS,MAAM,EAAkC,GAC7D,oBAAoB,EAAE,CAcxB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { LineBreak, LineBreakClasses, MAY_BREAK, PASS, Rules } from '@cto.af/linebreak';
|
|
1
|
+
import { LB28, LineBreak, LineBreakClasses, MAY_BREAK, PASS, Rules } from '@cto.af/linebreak';
|
|
2
|
+
import { getGraphemeEndPositions, getWordSegments } from './TextSegmentation.js';
|
|
2
3
|
const defaultRules = new Rules();
|
|
3
4
|
const emergencyRules = new Rules();
|
|
4
|
-
emergencyRules.
|
|
5
|
-
let wordSegmenter;
|
|
5
|
+
emergencyRules.rules = emergencyRules.rules.filter(rule => rule !== LB28);
|
|
6
6
|
/**
|
|
7
7
|
* Unicode 17.0 UAX #14 boundaries, expressed as UTF-16 offsets.
|
|
8
8
|
* The default profile is kept separate from dictionary and overflow tailoring
|
|
@@ -15,21 +15,25 @@ export function getDefaultLineBreaks(text) {
|
|
|
15
15
|
* Dictionary boundaries tailor complex-context scripts (Thai, Lao, Khmer).
|
|
16
16
|
* Emergency wrapping relaxes alphabetic word protection only; punctuation,
|
|
17
17
|
* non-breaking spaces and word joiners retain their Unicode constraints.
|
|
18
|
+
* Layout may reuse a paragraph's UTF-16 grapheme ends across both profiles.
|
|
18
19
|
*/
|
|
19
|
-
export function getWordLineBreaks(text, emergency = false) {
|
|
20
|
+
export function getWordLineBreaks(text, emergency = false, graphemeEnds = getGraphemeEndPositions(text)) {
|
|
20
21
|
const base = emergency ? emergencyRules : defaultRules;
|
|
21
22
|
if (emergency || !Array.from(text).some(character => isComplexContext(character.codePointAt(0)))) {
|
|
22
|
-
return
|
|
23
|
+
return collectProtectedBreaks(base, text, graphemeEnds);
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
const boundaries = new Set(Array.from(wordSegmenter.segment(text), segment => segment.index));
|
|
25
|
+
const boundaries = new Set(Array.from(getWordSegments(text), segment => segment.index));
|
|
26
26
|
const rules = new Rules();
|
|
27
|
-
rules.
|
|
27
|
+
rules.rules.splice(rules.rules.indexOf(LB28), 0, state => {
|
|
28
28
|
return boundaries.has(state.cur.len) && isComplexContext(state.cur.cp) && isComplexContext(state.next.cp)
|
|
29
29
|
? MAY_BREAK
|
|
30
30
|
: PASS;
|
|
31
31
|
});
|
|
32
|
-
return
|
|
32
|
+
return collectProtectedBreaks(rules, text, graphemeEnds);
|
|
33
|
+
}
|
|
34
|
+
function collectProtectedBreaks(rules, text, graphemeEnds) {
|
|
35
|
+
const boundaries = new Set(graphemeEnds);
|
|
36
|
+
return Array.from(rules.breaks(text), ({ position, required }) => ({ position, required })).filter(point => point.required || boundaries.has(point.position));
|
|
33
37
|
}
|
|
34
38
|
function isComplexContext(codePoint) {
|
|
35
39
|
return codePoint >= 0 && LineBreak.get(codePoint) === LineBreakClasses.SA;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LineBreaks.js","sourceRoot":"","sources":["../../../src/kurot/text/LineBreaks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"LineBreaks.js","sourceRoot":"","sources":["../../../src/kurot/text/LineBreaks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC9F,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAOjF,MAAM,YAAY,GAAG,IAAI,KAAK,EAAE,CAAC;AACjC,MAAM,cAAc,GAAG,IAAI,KAAK,EAAE,CAAC;AACnC,cAAc,CAAC,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAChD,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AACpG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAChC,IAAY,EACZ,SAAS,GAAG,KAAK,EACjB,eAAkC,uBAAuB,CAAC,IAAI,CAAC;IAE/D,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC;IACvD,IAAI,SAAS,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC,EAAE,CAAC;QACnG,OAAO,sBAAsB,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACxF,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;IAC1B,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE;QACxD,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACxG,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC;IACT,CAAC,CAAC,CAAC;IACH,OAAO,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAY,EAAE,IAAY,EAAE,YAA+B;IAC1F,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CACjG,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CACzD,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAiB;IAC1C,OAAO,SAAS,IAAI,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,gBAAgB,CAAC,EAAE,CAAC;AAC3E,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextLineLayout.d.ts","sourceRoot":"","sources":["../../../src/kurot/text/TextLineLayout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TextLineLayout.d.ts","sourceRoot":"","sources":["../../../src/kurot/text/TextLineLayout.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAA6B,MAAM,yBAAyB,CAAC;AAErG,MAAM,WAAW,qBAAqB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CACjB;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG,YAAY,EAAE,CAuJxG"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getWordLineBreaks } from './LineBreaks.js';
|
|
2
2
|
import { measureText } from './TextMeasurer.js';
|
|
3
|
+
import { getGraphemeEndPositions } from './TextSegmentation.js';
|
|
3
4
|
/**
|
|
4
5
|
* Layout uses the complete text so style boundaries cannot create line breaks.
|
|
5
6
|
* Soft-wrap separator spaces are omitted from painting and measured width,
|
|
@@ -39,9 +40,10 @@ export function layoutTextLines(elements, options) {
|
|
|
39
40
|
lines.push(line);
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
43
|
+
const graphemeEnds = getGraphemeEndPositions(paragraph);
|
|
42
44
|
const positions = options.wordWrap
|
|
43
|
-
? getWordLineBreaks(paragraph).map(point => start + point.position)
|
|
44
|
-
:
|
|
45
|
+
? getWordLineBreaks(paragraph, false, graphemeEnds).map(point => start + point.position)
|
|
46
|
+
: graphemeEnds.map(position => start + position);
|
|
45
47
|
let emergencyPositions;
|
|
46
48
|
let emergencyIndex = 0;
|
|
47
49
|
let oversizedBoundary = -1;
|
|
@@ -71,7 +73,7 @@ export function layoutTextLines(elements, options) {
|
|
|
71
73
|
candidateIndex++;
|
|
72
74
|
}
|
|
73
75
|
if (chosen === lineStart) {
|
|
74
|
-
emergencyPositions ??= getWordLineBreaks(paragraph, true).map(point => start + point.position);
|
|
76
|
+
emergencyPositions ??= getWordLineBreaks(paragraph, true, graphemeEnds).map(point => start + point.position);
|
|
75
77
|
const firstBoundary = positions[positionIndex];
|
|
76
78
|
oversizedBoundary = firstBoundary;
|
|
77
79
|
while (emergencyPositions[emergencyIndex] <= lineStart) {
|
|
@@ -139,13 +141,4 @@ export function layoutTextLines(elements, options) {
|
|
|
139
141
|
return { width, height, charNum: end - start, hasNextLine: false, elements: lineElements };
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
|
-
function characterPositions(text) {
|
|
143
|
-
const positions = [];
|
|
144
|
-
let position = 0;
|
|
145
|
-
for (const character of text) {
|
|
146
|
-
position += character.length;
|
|
147
|
-
positions.push(position);
|
|
148
|
-
}
|
|
149
|
-
return positions;
|
|
150
|
-
}
|
|
151
144
|
//# sourceMappingURL=TextLineLayout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextLineLayout.js","sourceRoot":"","sources":["../../../src/kurot/text/TextLineLayout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"TextLineLayout.js","sourceRoot":"","sources":["../../../src/kurot/text/TextLineLayout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function getWordSegments(text: string): Intl.Segments;
|
|
2
|
+
export declare function getGraphemeSegments(text: string): Intl.Segments;
|
|
3
|
+
/**
|
|
4
|
+
* End offsets use UTF-16 units, matching TextField's source and caret indices.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getGraphemeEndPositions(text: string): number[];
|
|
7
|
+
//# sourceMappingURL=TextSegmentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextSegmentation.d.ts","sourceRoot":"","sources":["../../../src/kurot/text/TextSegmentation.ts"],"names":[],"mappings":"AAGA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,QAAQ,CAG3D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,QAAQ,CAG/D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
let wordSegmenter;
|
|
2
|
+
let graphemeSegmenter;
|
|
3
|
+
export function getWordSegments(text) {
|
|
4
|
+
wordSegmenter ??= new Intl.Segmenter(undefined, { granularity: 'word' });
|
|
5
|
+
return wordSegmenter.segment(text);
|
|
6
|
+
}
|
|
7
|
+
export function getGraphemeSegments(text) {
|
|
8
|
+
graphemeSegmenter ??= new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
9
|
+
return graphemeSegmenter.segment(text);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* End offsets use UTF-16 units, matching TextField's source and caret indices.
|
|
13
|
+
*/
|
|
14
|
+
export function getGraphemeEndPositions(text) {
|
|
15
|
+
return Array.from(getGraphemeSegments(text), segment => segment.index + segment.segment.length);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=TextSegmentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TextSegmentation.js","sourceRoot":"","sources":["../../../src/kurot/text/TextSegmentation.ts"],"names":[],"mappings":"AAAA,IAAI,aAAyC,CAAC;AAC9C,IAAI,iBAA6C,CAAC;AAElD,MAAM,UAAU,eAAe,CAAC,IAAY;IAC3C,aAAa,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,OAAO,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC/C,iBAAiB,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;IACjF,OAAO,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IACnD,OAAO,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACjG,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WordWrap.d.ts","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"WordWrap.d.ts","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA6BH;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAsC/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CASrD"}
|
|
@@ -8,15 +8,8 @@
|
|
|
8
8
|
* Falls back to character-by-character splitting when `Intl.Segmenter` is
|
|
9
9
|
* unavailable.
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
|
-
let _segmenter;
|
|
11
|
+
import { getGraphemeSegments, getWordSegments } from './TextSegmentation.js';
|
|
13
12
|
const hasSegmenter = typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function';
|
|
14
|
-
function getSegmenter() {
|
|
15
|
-
if (!_segmenter) {
|
|
16
|
-
_segmenter = new Intl.Segmenter(undefined, { granularity: 'word' });
|
|
17
|
-
}
|
|
18
|
-
return _segmenter;
|
|
19
|
-
}
|
|
20
13
|
// ── Breaking space / newline tables ─────────────────────────────────────────
|
|
21
14
|
const BREAKING_SPACES = new Set([
|
|
22
15
|
0x0009, // tab
|
|
@@ -50,7 +43,7 @@ export function tokenize(text) {
|
|
|
50
43
|
return [];
|
|
51
44
|
if (hasSegmenter) {
|
|
52
45
|
const result = [];
|
|
53
|
-
for (const seg of
|
|
46
|
+
for (const seg of getWordSegments(text)) {
|
|
54
47
|
const s = seg.segment;
|
|
55
48
|
if (!s)
|
|
56
49
|
continue;
|
|
@@ -93,9 +86,8 @@ export function tokenize(text) {
|
|
|
93
86
|
*/
|
|
94
87
|
export function splitGraphemes(text) {
|
|
95
88
|
if (hasSegmenter) {
|
|
96
|
-
const seg = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
97
89
|
const result = [];
|
|
98
|
-
for (const s of
|
|
90
|
+
for (const s of getGraphemeSegments(text)) {
|
|
99
91
|
result.push(s.segment);
|
|
100
92
|
}
|
|
101
93
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WordWrap.js","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH
|
|
1
|
+
{"version":3,"file":"WordWrap.js","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"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"}
|
package/docs/ai-context.md
CHANGED
|
@@ -5,7 +5,8 @@ agent unfamiliar with Kurot does not need to re-derive the architecture from
|
|
|
5
5
|
scratch on every session. Treat the package source and its `src/index.ts`
|
|
6
6
|
barrel as the authority for current behavior and exports.
|
|
7
7
|
|
|
8
|
-
Package identity: `@kurot/core@2.2.
|
|
8
|
+
Package identity: `@kurot/core@2.2.1`. This patch is prepared for publication;
|
|
9
|
+
Core 2.2.0 is published. It provides Kurot's scene graph,
|
|
9
10
|
events, rendering, text, resource, network and media runtime. Rendering uses a
|
|
10
11
|
flat `InstructionSet + RenderPipe` pipeline. ES2022 / evergreen browsers only
|
|
11
12
|
with `strict: true`. Two
|
|
@@ -42,7 +43,8 @@ src/kurot/
|
|
|
42
43
|
│ CustomFilter, MultiPassFilter, BloomFilter. GPU execution in player/webgl/;
|
|
43
44
|
│ push/pop scene integration in player/pipes/.
|
|
44
45
|
├── text/ TextField, BitmapText/BitmapFont, StageText (DOM overlay, INPUT mode only),
|
|
45
|
-
│ HtmlTextParser, InputController, TextMeasurer,
|
|
46
|
+
│ HtmlTextParser, InputController, TextMeasurer, TextSegmentation,
|
|
47
|
+
│ LineBreaks, TextLineLayout, WordWrap.
|
|
46
48
|
├── resource/ Resource class + `resource` singleton, ResourceLoader, analyzers/
|
|
47
49
|
│ (Image/Json/Text/Sound/Sheet/Font). Async, resource.json-driven (RES-compatible).
|
|
48
50
|
├── net/ HttpRequest, ImageLoader. Low-level; resource/analyzers build on these.
|
|
@@ -203,7 +205,7 @@ Re-export order: `events`, `geom`, `utils`, `display`, `net`, `filters`,
|
|
|
203
205
|
| Change how instructions are built/executed | `player/webgl/WebGLRenderer.ts`, `player/InstructionSet.ts` |
|
|
204
206
|
| Add a new resource type/parser | `resource/analyzers/`, register in `Resource.ts` |
|
|
205
207
|
| Debug a texture-batching issue | `player/webgl/MultiTextureBatcher.ts`, `player/webgl/WebGLDrawCmdManager.ts` |
|
|
206
|
-
| Change text layout/wrapping | `text/LineBreaks.ts`, `text/TextLineLayout.ts`, `text/TextMeasurer.ts`; `docs/text-layout.md`
|
|
208
|
+
| Change text layout/wrapping | `text/LineBreaks.ts`, `text/TextLineLayout.ts`, `text/TextSegmentation.ts`, `text/TextMeasurer.ts`; `docs/text-layout.md` |
|
|
207
209
|
| Understand dirty-flag propagation | `display/DisplayObject.ts` (`$markDirty`, `$cacheDirtyUp`, `$renderDirtyUp`) |
|
|
208
210
|
| Run perf tests | `examples/benchmark/`, `pnpm benchmark`; automated Kurot/PixiJS/Egret comparison via `pnpm benchmark:compare` |
|
|
209
211
|
|
|
@@ -220,6 +222,13 @@ with local index remapping (player/webgl/split-mesh.ts).
|
|
|
220
222
|
|
|
221
223
|
Word-wrapped text uses Unicode 17.0 UAX #14, dictionary tailoring for SA scripts
|
|
222
224
|
and alphabetic overflow tailoring. Style runs never introduce break positions.
|
|
225
|
+
Core 2.2.1 tailors rules by imported function identity and filters optional
|
|
226
|
+
breaks through grapheme boundaries. Character wrapping also uses complete
|
|
227
|
+
graphemes. Never introduce a break inside a cluster; an oversized cluster
|
|
228
|
+
overflows intact. The untailored default profile stays unchanged for Unicode
|
|
229
|
+
conformance. `TextSegmentation.ts` shares cached word/grapheme segmenters;
|
|
230
|
+
ordinary and emergency layout reuse a paragraph's UTF-16 grapheme end offsets.
|
|
231
|
+
No text or segmentation results are retained globally. See [text layout](text-layout.md).
|
|
223
232
|
Automatic-wrap spaces count in source offsets but not painted width. Core's
|
|
224
233
|
Canvas renderer advances input indices by line.charNum, including hidden spaces
|
|
225
234
|
and CRLF; do not derive the next line's source offset from painted text lengths.
|
|
@@ -254,4 +263,4 @@ The dependency is published @kurot/bitmap-font ^0.1.0, with no local override.
|
|
|
254
263
|
BitmapText.measureText() leaves rendered constraints and layout caches unchanged;
|
|
255
264
|
text borrows the font and the resource/caller retains ownership. Font pages are
|
|
256
265
|
full, unrotated textures at scale factor 1. Core 2.1.1 does not include these
|
|
257
|
-
changes.
|
|
266
|
+
changes. Core 2.2.0 is published with its registry bitmap-font dependency.
|
package/docs/architecture.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @kurot/core architecture
|
|
2
2
|
|
|
3
|
-
> Package version: 2.2.
|
|
3
|
+
> Package version: 2.2.1. See [CHANGELOG.md](../CHANGELOG.md) for release notes.
|
|
4
4
|
> The [AI context map](./ai-context.md) covers directories, non-obvious behavior,
|
|
5
5
|
> terminology and task-to-file references. This document explains design choices
|
|
6
6
|
> and internal mechanisms for contributors; the two documents complement each other.
|
|
@@ -14,10 +14,16 @@ resources, networking and media runtime. Public APIs follow Egret's
|
|
|
14
14
|
`DisplayObject` and event model, while rendering uses a flat
|
|
15
15
|
`InstructionSet + RenderPipe` pipeline inspired by Pixi.js 8.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Since 2.2.0, Core uses published `@kurot/bitmap-font ^0.1.0` as the headless font-data
|
|
18
18
|
and layout kernel. Core manages font resources, image-page ownership and glyph
|
|
19
19
|
drawing in both backends. UI components belong to the UI package.
|
|
20
20
|
|
|
21
|
+
TextField uses Unicode 17.0 line-break rules with dictionary and overflow
|
|
22
|
+
tailoring. Since 2.2.1, tailoring uses imported rule identities, and layout
|
|
23
|
+
filters optional break positions through `Intl.Segmenter` grapheme boundaries.
|
|
24
|
+
Word and grapheme segmenters are cached; paragraph results remain local to
|
|
25
|
+
each layout. See [text layout](text-layout.md).
|
|
26
|
+
|
|
21
27
|
| Aspect | Description |
|
|
22
28
|
| ------------------ | ---------------------------------------------------------- |
|
|
23
29
|
| Modules | ES modules (`@kurot/core` npm package) |
|
|
@@ -96,6 +102,7 @@ packages/core/src/kurot/
|
|
|
96
102
|
│ ├── HtmlTextParser.ts # HTML text parsing
|
|
97
103
|
│ ├── InputController.ts # selection, caret and keyboard input
|
|
98
104
|
│ ├── TextMeasurer.ts # measureText and getFontString
|
|
105
|
+
│ ├── TextSegmentation.ts # shared cached word/grapheme segmenters and UTF-16 boundaries
|
|
99
106
|
│ ├── LineBreaks.ts # Unicode 17.0 UAX #14 boundaries and dictionary segmentation
|
|
100
107
|
│ ├── TextLineLayout.ts # rich-text wrapping, width fitting and UTF-16 source offsets
|
|
101
108
|
│ ├── WordWrap.ts # independent token and grapheme utilities
|
package/docs/text-layout.md
CHANGED
|
@@ -10,6 +10,10 @@ additional wrap opportunities.
|
|
|
10
10
|
and closing punctuation rules rather than treating dictionary words as units.
|
|
11
11
|
- Thai, Lao and Khmer add dictionary boundaries through the browser's
|
|
12
12
|
`Intl.Segmenter`. The default Unicode rules remain independently testable.
|
|
13
|
+
- Since Core 2.2.1, optional word and emergency break opportunities are filtered
|
|
14
|
+
through `Intl.Segmenter` grapheme boundaries. Emoji ZWJ sequences, flags,
|
|
15
|
+
skin-tone modifiers, combining marks and conjuncts stay intact. A grapheme
|
|
16
|
+
wider than the field overflows intact rather than being split.
|
|
13
17
|
- When an alphabetic word is wider than the entire line, emergency wrapping
|
|
14
18
|
relaxes UAX #14's alphabetic rule LB28. Non-breaking spaces, narrow
|
|
15
19
|
non-breaking spaces, word joiners and punctuation protection remain in force.
|
|
@@ -24,13 +28,18 @@ additional wrap opportunities.
|
|
|
24
28
|
- In multiline mode, CRLF, CR, LF, VT, FF, NEL, line separator and paragraph
|
|
25
29
|
separator force line breaks. A trailing hard separator retains a final empty line.
|
|
26
30
|
|
|
27
|
-
`wordWrap = false`
|
|
31
|
+
`wordWrap = false` wraps between grapheme clusters in multiline mode since
|
|
32
|
+
Core 2.2.1; earlier releases wrapped between code points. Since
|
|
28
33
|
Core 2.1.0, explicit `multiline = false` renders only the first
|
|
29
34
|
hard-separated line without width wrapping. Dynamic text defaults to multiline;
|
|
30
35
|
INPUT defaults to single-line until the flag is assigned. An unconstrained
|
|
31
36
|
multiline field measures its hard-separated lines without automatic wrapping.
|
|
32
37
|
Fixed height clips drawing rather than changing those measured lines.
|
|
33
38
|
|
|
39
|
+
Dictionary and grapheme segmentation follow the browser's `Intl` data. The
|
|
40
|
+
pinned Unicode 17.0 tables govern the line-break profile; they do not replace
|
|
41
|
+
the browser's grapheme implementation.
|
|
42
|
+
|
|
34
43
|
## Font readiness
|
|
35
44
|
|
|
36
45
|
Load fonts before constructing or measuring text where possible. After a font
|
|
@@ -48,7 +57,11 @@ string is retained, including later hard-separated lines hidden in single-line
|
|
|
48
57
|
mode. Existing files are not rewritten automatically.
|
|
49
58
|
|
|
50
59
|
`WordWrap.tokenize()` remains a public word-segmentation utility; it is not the
|
|
51
|
-
TextField line-break algorithm. `splitGraphemes()`
|
|
60
|
+
TextField line-break algorithm. `splitGraphemes()` returns complete grapheme
|
|
61
|
+
clusters. These utilities and TextField share cached word/grapheme segmenters.
|
|
62
|
+
Layout computes grapheme end offsets once per constrained paragraph and reuses
|
|
63
|
+
them for ordinary and emergency wrapping. Text and segmentation results are
|
|
64
|
+
not cached globally.
|
|
52
65
|
|
|
53
66
|
## Validation
|
|
54
67
|
|
|
@@ -57,6 +70,18 @@ cases against the untailored profile. The compressed fixture includes its
|
|
|
57
70
|
source URL, SHA-256 and Unicode license. Dictionary and emergency tailoring,
|
|
58
71
|
multilingual width fitting, rich text, input offsets and rendering are tested
|
|
59
72
|
separately in `test/TextWrapping.test.ts`.
|
|
73
|
+
`test/TextGraphemes.test.ts` covers complete-character wrapping, oversized
|
|
74
|
+
clusters, rich-text boundaries, hard separators and UTF-16 input offsets.
|
|
75
|
+
`test/LineBreaks.test.ts` also builds and executes a minified browser bundle
|
|
76
|
+
with `keepNames: false` to verify dictionary, alphabetic, grapheme and
|
|
77
|
+
non-breaking protection. The test environment provides browser APIs without
|
|
78
|
+
Node's `Buffer` global.
|
|
79
|
+
|
|
80
|
+
The 2.2.1 release check installs the packed Core with published UI 3.2.0 and
|
|
81
|
+
DragonBones 0.1.0, checks their declarations together, and renders a complete
|
|
82
|
+
minified browser bundle with `keepNames: false` in Canvas 2D, WebGL 1 and
|
|
83
|
+
WebGL 2. It verifies real-font grapheme layout, dictionary/emergency wrapping,
|
|
84
|
+
BitmapLabel glyphs and an animated native DragonBones mesh.
|
|
60
85
|
|
|
61
86
|
The dependency bundles its Unicode property tables and uses no network or Node
|
|
62
87
|
runtime APIs. Both WebGL text rasterization and Canvas rendering share this
|
|
@@ -65,15 +90,51 @@ regressions. No resource-manifest or CLI XML-format change is involved.
|
|
|
65
90
|
|
|
66
91
|
## Package dependencies
|
|
67
92
|
|
|
68
|
-
UI 3.
|
|
69
|
-
game 2.0.0 declares `^2.0.0`.
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
Published UI 3.2.0 requires Core `^2.2.0` for BitmapLabel. Older UI 3.1.0 and
|
|
94
|
+
ui-runtime 0.8.2 declare Core `^2.1.0`; game 2.0.0 declares `^2.0.0`.
|
|
95
|
+
Existing example lockfiles still resolve Core 2.1.1 and UI 3.1.0; accepting a
|
|
96
|
+
version in a dependency range does not update an installed package.
|
|
97
|
+
DragonBones 0.1.0 declares Core `^2.1.1` and develops against published Core
|
|
98
|
+
2.2.0. Its animation adapter does not depend on TextField wrapping changes.
|
|
99
|
+
CLI templates use `latest`.
|
|
72
100
|
Label font shrinking calls `invalidateTextMetrics()` and requires Core 2.1.0
|
|
73
|
-
or later. Core 2.2.
|
|
101
|
+
or later. Core 2.2.1 preserves that TextField contract and remains accepted by
|
|
74
102
|
these existing peer ranges. Bitmap-font rendering is separate from TextField;
|
|
75
103
|
see [bitmap fonts](bitmap-fonts.md). The new UI BitmapLabel requires Core 2.2.0
|
|
76
|
-
and
|
|
104
|
+
and published UI 3.2.0; published UI 3.1.0 does not contain it.
|
|
105
|
+
|
|
106
|
+
Current consumers still need an installed-version update after 2.2.1 is published:
|
|
107
|
+
|
|
108
|
+
| Consumer | Current Core declaration | Locked or pinned Core | Required update |
|
|
109
|
+
| --- | --- | --- | --- |
|
|
110
|
+
| UI development | `^2.2.0` | 2.2.0 | Refresh the lockfile. |
|
|
111
|
+
| DragonBones development | `2.2.0`; peer `^2.1.1` | 2.2.0 | Update the development pin and lockfile. The peer already accepts 2.2.1. |
|
|
112
|
+
| Game / ui-runtime development | `^2.1.1` | 2.1.1 | Refresh the lockfiles. Their peers already accept 2.2.1. |
|
|
113
|
+
| Engine examples / tentax / copied Reskin project | `^2.1.1` | 2.1.1 | Refresh application lockfiles. |
|
|
114
|
+
| Editor | `2.1.1` | Exact pin 2.1.1 | Update the dependency and rebuild to deliver the fix. |
|
|
115
|
+
| Reskin application / milf-master template | `^2.1.1` | No pnpm lockfile | Update the installation; the declared range accepts 2.2.1. |
|
|
116
|
+
|
|
117
|
+
These consumer packages and applications are not changed by the Core patch.
|
|
118
|
+
|
|
119
|
+
## Updating from Core 2.2.0 to 2.2.1
|
|
120
|
+
|
|
121
|
+
The 2.2.0 wrapper customizes rules by function name. Minifiers that rename
|
|
122
|
+
functions can silently disable alphabetic overflow tailoring and cause SA-script
|
|
123
|
+
dictionary wrapping to throw `Rule not found: "LB28"`. Core 2.2.1 customizes
|
|
124
|
+
the pinned dependency's rules by imported function identity, so this text path
|
|
125
|
+
no longer requires preserved function names. Kurot CLI's existing
|
|
126
|
+
`keepNames: true` setting remains useful for other name-based consumer APIs;
|
|
127
|
+
this patch does not change the CLI build contract.
|
|
128
|
+
|
|
129
|
+
Passing the official default corpus does not cover every real-text sequence.
|
|
130
|
+
The dependency exposes internal break opportunities for some composed emoji,
|
|
131
|
+
including family ZWJ sequences. Core 2.2.1 filters optional layout opportunities
|
|
132
|
+
through grapheme boundaries and uses those boundaries for character wrapping.
|
|
133
|
+
The untailored default Unicode profile remains unchanged. This affects
|
|
134
|
+
TextField layout without changing DragonBones playback or the separate
|
|
135
|
+
bitmap-font layout contract. No asset or project migration is required; update
|
|
136
|
+
the installed package and lockfile after publication. Source 2.2.1 is prepared
|
|
137
|
+
for publication; Core 2.2.0 is the published release.
|
|
77
138
|
|
|
78
139
|
References: [UAX #14 revision 55](https://www.unicode.org/reports/tr14/tr14-55.html),
|
|
79
140
|
[official corpus](https://www.unicode.org/Public/17.0.0/ucd/auxiliary/LineBreakTest.txt),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kurot/core",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Modern rewrite of the Egret game engine — WebGL multi-texture batching, instruction-driven render pipeline, strict TypeScript, Egret-compatible API",
|
|
5
5
|
"author": "Kelvin <kyia.x52@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@playwright/test": "1.62.1",
|
|
46
46
|
"@types/node": "^22.0.0",
|
|
47
|
+
"esbuild": "^0.28.2",
|
|
47
48
|
"fast-check": "^4.6.0",
|
|
48
49
|
"happy-dom": "^20.8.9",
|
|
49
50
|
"pixi.js": "8.20.0",
|