@kurot/core 2.0.0 → 2.1.0
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 +56 -0
- package/README.md +44 -20
- package/dist/kurot/player/canvas/CanvasRenderer.d.ts.map +1 -1
- package/dist/kurot/player/canvas/CanvasRenderer.js +7 -2
- package/dist/kurot/player/canvas/CanvasRenderer.js.map +1 -1
- package/dist/kurot/text/LineBreaks.d.ts +17 -0
- package/dist/kurot/text/LineBreaks.d.ts.map +1 -0
- package/dist/kurot/text/LineBreaks.js +37 -0
- package/dist/kurot/text/LineBreaks.js.map +1 -0
- package/dist/kurot/text/TextField.d.ts +15 -1
- package/dist/kurot/text/TextField.d.ts.map +1 -1
- package/dist/kurot/text/TextField.js +40 -132
- package/dist/kurot/text/TextField.js.map +1 -1
- package/dist/kurot/text/TextLineLayout.d.ts +18 -0
- package/dist/kurot/text/TextLineLayout.d.ts.map +1 -0
- package/dist/kurot/text/TextLineLayout.js +151 -0
- package/dist/kurot/text/TextLineLayout.js.map +1 -0
- package/dist/kurot/text/WordWrap.d.ts +5 -7
- package/dist/kurot/text/WordWrap.d.ts.map +1 -1
- package/dist/kurot/text/WordWrap.js +5 -7
- package/dist/kurot/text/WordWrap.js.map +1 -1
- package/docs/ai-context.md +238 -0
- package/docs/architecture.md +373 -0
- package/docs/filters.md +232 -0
- package/docs/resource.md +333 -0
- package/docs/text-layout.md +77 -0
- package/package.json +5 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Locale-aware word segmentation utility.
|
|
3
3
|
*
|
|
4
4
|
* Uses `Intl.Segmenter` (supported in all browsers since 2024) for locale-aware
|
|
5
|
-
* word segmentation
|
|
6
|
-
*
|
|
7
|
-
* and mixed-script text.
|
|
5
|
+
* word segmentation, including dictionary-based boundaries. Word boundaries
|
|
6
|
+
* are distinct from Unicode line-break opportunities; TextField uses LineBreaks.
|
|
8
7
|
*
|
|
9
8
|
* Falls back to character-by-character splitting when `Intl.Segmenter` is
|
|
10
9
|
* unavailable.
|
|
@@ -38,7 +37,7 @@ const BREAKING_SPACES = new Set([
|
|
|
38
37
|
const NEWLINES = new Set([0x000a, 0x000d]);
|
|
39
38
|
// ── Public API ───────────────────────────────────────────────────────────────
|
|
40
39
|
/**
|
|
41
|
-
* Split text into word / space segments
|
|
40
|
+
* Split text into locale-aware word / space segments.
|
|
42
41
|
*
|
|
43
42
|
* Returns plain strings — each string is either a word or a space.
|
|
44
43
|
* Newlines are NOT included (the caller should split by newlines first).
|
|
@@ -90,8 +89,7 @@ export function tokenize(text) {
|
|
|
90
89
|
}
|
|
91
90
|
/**
|
|
92
91
|
* Split a string into grapheme clusters (user-perceived characters).
|
|
93
|
-
*
|
|
94
|
-
* character-by-character.
|
|
92
|
+
* This utility is independent of TextField's UAX #14 line layout.
|
|
95
93
|
*/
|
|
96
94
|
export function splitGraphemes(text) {
|
|
97
95
|
if (hasSegmenter) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WordWrap.js","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"WordWrap.js","sourceRoot":"","sources":["../../../src/kurot/text/WordWrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# @kurot/core — AI context map
|
|
2
|
+
|
|
3
|
+
Read this before exploring `src/`. It is a compressed map of the package so an
|
|
4
|
+
agent unfamiliar with Kurot does not need to re-derive the architecture from
|
|
5
|
+
scratch on every session. Treat the package source and its `src/index.ts`
|
|
6
|
+
barrel as the authority for current behavior and exports.
|
|
7
|
+
|
|
8
|
+
Package identity: `@kurot/core@2.1.0`. It provides Kurot's scene graph,
|
|
9
|
+
events, rendering, text, resource, network and media runtime. Rendering uses a
|
|
10
|
+
flat `InstructionSet + RenderPipe` pipeline. ES2022 / evergreen browsers only
|
|
11
|
+
with `strict: true`. Two
|
|
12
|
+
rendering backends: WebGL (primary, two-phase build/execute, InstructionSet-
|
|
13
|
+
driven) and Canvas 2D (fallback, direct scene-graph traversal every frame — no
|
|
14
|
+
InstructionSet, no dirty-flag optimization).
|
|
15
|
+
|
|
16
|
+
Source root: `src/kurot/`. Public API: `src/index.ts` re-exports each
|
|
17
|
+
subfolder's `index.ts` — see §4 for the full grouped list before assuming
|
|
18
|
+
something isn't exported.
|
|
19
|
+
|
|
20
|
+
## 1. Directory map
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
src/kurot/
|
|
24
|
+
├── display/ Scene graph: DisplayObject → DisplayObjectContainer → Sprite/Stage,
|
|
25
|
+
│ Bitmap, Shape, Mesh, Graphics (flat GraphicsCommand[], not a node tree),
|
|
26
|
+
│ texture/ (BitmapData, Texture, RenderTexture, SpriteSheet).
|
|
27
|
+
│ Defines the retained tree the renderer reads from; does not render itself.
|
|
28
|
+
├── player/ Game loop + both render backends. Player, createPlayer(), SystemTicker/
|
|
29
|
+
│ ticker singleton, ScreenAdapter, TouchHandler. Backend-neutral abstractions
|
|
30
|
+
│ live here: RenderPipe / RenderContext / RenderBuffer interfaces (the latter
|
|
31
|
+
│ two are internal), InstructionSet, and pipes/ (Bitmap/Graphics/Mesh/Text/
|
|
32
|
+
│ Filter/Mask/Particle). The WebGL instruction renderer consumes the pipes.
|
|
33
|
+
│ ├── webgl/ WebGLRenderer, WebGLRenderContext, WebGLRenderBuffer/Target,
|
|
34
|
+
│ │ WebGLVertexArrayObject, WebGLDrawCmdManager, MultiTextureBatcher,
|
|
35
|
+
│ │ shaders/ (ShaderLib GLSL 1.00, ShaderLib2 GLSL 3.00). The WebGL-only execute path.
|
|
36
|
+
│ └── canvas/ CanvasRenderer (fallback AND a dependency of the WebGL path — see §2),
|
|
37
|
+
│ DisplayList (offscreen cache backing cacheAsBitmap), CanvasBuffer.
|
|
38
|
+
├── events/ Event, EventDispatcher (capture/bubble, once()), EventPhase,
|
|
39
|
+
│ 8 concrete subclasses (TouchEvent, TimerEvent, ProgressEvent, etc.).
|
|
40
|
+
├── geom/ Matrix, Point, Rectangle + shared*/create()/release() object pools.
|
|
41
|
+
├── filters/ Filter base + BlurFilter, GlowFilter, DropShadowFilter, ColorMatrixFilter,
|
|
42
|
+
│ CustomFilter, MultiPassFilter, BloomFilter. GPU execution in player/webgl/;
|
|
43
|
+
│ push/pop scene integration in player/pipes/.
|
|
44
|
+
├── text/ TextField, BitmapText/BitmapFont, StageText (DOM overlay, INPUT mode only),
|
|
45
|
+
│ HtmlTextParser, InputController, TextMeasurer, LineBreaks, TextLineLayout, WordWrap.
|
|
46
|
+
├── resource/ Resource class + `resource` singleton, ResourceLoader, analyzers/
|
|
47
|
+
│ (Image/Json/Text/Sound/Sheet). Async, resource.json-driven (RES-compatible).
|
|
48
|
+
├── net/ HttpRequest, ImageLoader. Low-level; resource/analyzers build on these.
|
|
49
|
+
├── media/ Sound (+SoundChannel), Video. Web Audio + HTMLAudioElement fallback.
|
|
50
|
+
├── system/ Capabilities (static). Must be _init()'d — createPlayer() does this for you.
|
|
51
|
+
├── utils/ ByteArray, Timer, Logger, FontManager, DebugLog, Base64Util, NumberUtils.
|
|
52
|
+
│ No engine-wide numeric object identity API is provided.
|
|
53
|
+
├── localStorage/ Plain functions (getItem/setItem/removeItem/clear), exported as a namespace.
|
|
54
|
+
└── external/ ExternalInterface — bridge to window.* host callbacks.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The dev-only benchmark page, runtime, adapters, vendor baseline, and tests are
|
|
58
|
+
colocated under `examples/benchmark/`; none are exported from `index.ts`.
|
|
59
|
+
|
|
60
|
+
## 2. Non-obvious current behavior
|
|
61
|
+
|
|
62
|
+
- `DisplayObject.matrix` getter returns a **clone**, not a live reference.
|
|
63
|
+
Mutating the returned matrix does nothing — assign it back or use `$setMatrix`.
|
|
64
|
+
- `getChildAt`/`removeChildAt` return `undefined` on out-of-bounds instead of
|
|
65
|
+
throwing. `removeChildren()` returns `void`, not the removed array.
|
|
66
|
+
- `blendMode` string values are Canvas 2D composite-operation names;
|
|
67
|
+
the normal value is `"source-over"`.
|
|
68
|
+
- `cacheAsBitmap` is a pure alias for `cacheAsTexture(true)` — there is no
|
|
69
|
+
separate legacy path. It **always** rasterizes to an offscreen Canvas 2D
|
|
70
|
+
surface first (`DisplayList`), then optionally re-uploads as a GL texture.
|
|
71
|
+
It never means "cache directly to a GPU framebuffer."
|
|
72
|
+
- `DisplayObjectContainer.isRenderGroup` has **zero visual effect on Canvas 2D
|
|
73
|
+
rendering**. It's a WebGL-only optimization hint that isolates a subtree
|
|
74
|
+
into its own `InstructionSet` — toggling it never changes pixels, only which
|
|
75
|
+
instruction set absorbs rebuilds.
|
|
76
|
+
- `CanvasRenderer` is not purely a fallback. The WebGL backend depends on it
|
|
77
|
+
internally to rasterize Graphics/Text to offscreen canvases before texture
|
|
78
|
+
upload, and to snapshot `RenderTexture`. Don't reason about it as dead code
|
|
79
|
+
when WebGL is active.
|
|
80
|
+
- The engine is **single-Player by design**. `Player`'s constructor wires
|
|
81
|
+
static hook fields directly onto `DisplayObject`/`DisplayObjectContainer`
|
|
82
|
+
(`$onStructureChange`, etc.) — there is no listener registry. A second
|
|
83
|
+
`Player` instance clobbers the first one's hooks.
|
|
84
|
+
- The `WebGLRenderBuffer.release(WebGLRenderBuffer.create(...))` line at the
|
|
85
|
+
end of `WebGLRenderer.render()` looks like dead code but isn't — it resets
|
|
86
|
+
the GL viewport/projection after an in-frame offscreen activation (filter,
|
|
87
|
+
mask, cacheAsBitmap). It's marked "DO NOT delete" in the source for a reason.
|
|
88
|
+
- `RenderPipe.destroyRenderable()` (immediate GPU resource release) is **not**
|
|
89
|
+
called automatically on removal from stage, because `$onRemoveFromStage`
|
|
90
|
+
also fires for temporary removals (e.g. virtualized lists). `GraphicsPipe`
|
|
91
|
+
and `TextPipe` instead rely on a `FinalizationRegistry` to `gl.deleteTexture()`
|
|
92
|
+
when the JS object is actually garbage-collected.
|
|
93
|
+
- `Resource` deliberately does **not** extend `EventDispatcher` — it has its
|
|
94
|
+
own `on`/`off`/`onProgress` API. Don't assume every "event-ish" class in
|
|
95
|
+
this codebase shares one dispatch shape.
|
|
96
|
+
- `resource.onProgress(cb)` listeners are permanent (never auto-removed); use
|
|
97
|
+
`loadGroup()`'s own `onProgress` param for one-off tracking. Concurrent
|
|
98
|
+
`loadGroup()` calls serialize through one internal `ResourceLoader`, they do
|
|
99
|
+
not run in parallel.
|
|
100
|
+
- `Base64Util.encode()` takes an `ArrayBuffer`, not a `string`
|
|
101
|
+
(`encode(new TextEncoder().encode(str))`).
|
|
102
|
+
- `Capabilities` must be initialized (`Capabilities._init()`) before use.
|
|
103
|
+
`createPlayer()` does this automatically; constructing `Player` directly
|
|
104
|
+
bypasses it and leaves capability queries stale.
|
|
105
|
+
- `StageText` (a real DOM `<input>` overlaid on the canvas) is only used for
|
|
106
|
+
`TextFieldType.INPUT` — the rest of `TextField` is fully canvas/GPU-drawn.
|
|
107
|
+
- GPU filter arrays execute in order. `CustomFilter.from()` takes explicit
|
|
108
|
+
WebGL 1/2 sources with numeric uniforms and auxiliary full-image BitmapData
|
|
109
|
+
bindings. `MultiPassFilter` retains earlier/original inputs for an acyclic
|
|
110
|
+
pass graph; `BloomFilter` is an LDR extraction/blur/composite example.
|
|
111
|
+
See `docs/filters.md` for exact UV, alpha, resolution and resource contracts.
|
|
112
|
+
- `BlurFilter.quality` controls pass pairs (1–16); large radii downsample to
|
|
113
|
+
stay within the existing 32-physical-pixel shader tier. Glow/DropShadow keep
|
|
114
|
+
their existing fixed-sample shader and do not use their quality metadata.
|
|
115
|
+
- GPU filters are skipped by Canvas 2D, including the Canvas capture used by
|
|
116
|
+
cacheAsTexture and RenderTexture. Do not cache GPU effects through those APIs.
|
|
117
|
+
- Filter setters invalidate weakly attached users. Direct uniform/binding edits
|
|
118
|
+
need `filter.invalidate()`. Auxiliary canvas/video edits additionally need
|
|
119
|
+
`BitmapData.invalidate(source)` for upload refresh.
|
|
120
|
+
- Intermediate filter targets share a 16-entry / 64 MiB idle pool. Active targets,
|
|
121
|
+
subtree captures and auxiliary uploads are separate. Player.destroy releases
|
|
122
|
+
idle effect targets and auxiliary uploads. Shader programs are cached per GL
|
|
123
|
+
context and source; compile/link failures throw and abort the failed frame.
|
|
124
|
+
- `Video` extends `Bitmap` and is directly renderable. It swaps between poster
|
|
125
|
+
and video textures, invalidates `BitmapData` on each available video frame,
|
|
126
|
+
and uses `requestAnimationFrame` only when `requestVideoFrameCallback` is
|
|
127
|
+
unavailable.
|
|
128
|
+
- hitTest / lookup APIs return `undefined`, never `null`, throughout — check
|
|
129
|
+
`=== undefined`.
|
|
130
|
+
|
|
131
|
+
## 3. Domain-specific terminology
|
|
132
|
+
|
|
133
|
+
| Term | Definition | Where defined |
|
|
134
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
135
|
+
| `InstructionSet` | Flat, reusable array of `Instruction` objects representing one frame's draw ops for a subtree, replacing scene-graph traversal at execute time. Tracks `structureDirty`, `dirtyRenderables`, and a `renderableIndex: Map<DisplayObject, number\|number[]>` for O(1) lookup during incremental patches. | `player/InstructionSet.ts` |
|
|
136
|
+
| `RenderPipe` | Interface implemented once per display-object category (`BitmapPipe`, `GraphicsPipe`, `MeshPipe`, `TextPipe`, `FilterPipe`, `MaskPipe`, `ParticlePipe`). It defines `addToInstructionSet()` (build), `updateRenderable()` (patch), and optional `destroyRenderable()` cleanup. The current renderer does not invoke cleanup merely because an object leaves the stage. Instructions are dispatched by `renderPipeId`. | `player/RenderPipe.ts`, implementations in `player/pipes/` |
|
|
137
|
+
| `structureDirty` vs `renderDirty` | `structureDirty` (on `InstructionSet`): topology changed (child added/removed/reordered, filter/mask changed) → full rebuild. `renderDirty` (on `DisplayObject`, propagated via `$renderDirtyUp()`): only visual data changed (position/alpha/tint/texture) on an object whose instruction-list slot is still valid → patch only, no rebuild. | Propagated in `display/DisplayObject.ts` (`$markDirty()`, `$cacheDirtyUp()`, `$renderDirtyUp()`); consumed in `player/webgl/WebGLRenderer.ts` `render()` |
|
|
138
|
+
| `RenderGroup` | A `DisplayObjectContainer` with `isRenderGroup = true`. Gets its own independent `InstructionSet` (tracked via `WeakMap`/`WeakRef` in `WebGLRenderer`). Structural changes inside the group never force the parent to rebuild — the parent set holds a single `renderGroup` instruction pointing at the child set. | `isRenderGroup` field: `display/DisplayObjectContainer.ts`; group handling: `player/webgl/WebGLRenderer.ts` (`_buildRenderGroup()`, `markStructureDirty()`, `markRenderableDirty()`) |
|
|
139
|
+
| `cacheAsTexture` vs `cacheAsBitmap` | `cacheAsBitmap` is a pure alias for `cacheAsTexture(true/false)`. `cacheAsTexture(options)` accepts `{resolution?, scaleMode?}` and creates a `DisplayList` (offscreen Canvas 2D buffer), reused across frames until dirty. On WebGL it's drawn as a single `displayListCache` instruction; rasterization still goes through `CanvasRenderer`, then the result is uploaded as a GL texture. | `display/DisplayObject.ts`; `player/canvas/DisplayList.ts`; `player/webgl/WebGLRenderer.ts` (`_executeDisplayListCache()`) |
|
|
140
|
+
| `DrawCmdManager` (`WebGLDrawCmdManager`) | Queue of `DrawCmd` records (12 types: TEXTURE, RECT, PUSH*MASK, POP_MASK, BLEND, RESIZE_TARGET, CLEAR_COLOR, ACT_BUFFER, ENABLE_SCISSOR, DISABLE_SCISSOR, SMOOTHING, MULTI_TEXTURE) sitting between `RenderPipe`s and actual GL calls. Auto-merges consecutive compatible commands. `WebGLRenderContext._flush()` walks it once per frame. This is the real batching layer — distinct from `MultiTextureBatcher`, which only assigns texture \_slots* for the `MULTI_TEXTURE` command. | `player/webgl/WebGLDrawCmdManager.ts` |
|
|
141
|
+
| `WebGLRenderContext` lifecycle | Constructed directly by `Player` (no factory/singleton). Auto-selects `webgl2` (ShaderLib2, GLSL ES 3.00) falling back to `webgl` (ShaderLib, GLSL ES 1.00). Listens for `webglcontextlost`/`webglcontextrestored`; on restore, re-creates GPU buffers, clears the shader cache, invalidates tracked `BitmapData.webGLTexture` refs, and tells `WebGLRenderer` to force a full instruction rebuild. | `player/webgl/WebGLRenderContext.ts` |
|
|
142
|
+
| `Player` vs `Stage` | `Stage` is a `DisplayObjectContainer` subclass: root of the scene graph, holds `stageWidth`/`stageHeight`/`scaleMode`/`orientation`/`frameRate` (proxies to the global `ticker`). No rendering logic itself. `Player` owns the `<canvas>`, constructs the WebGL-or-Canvas2D renderer, registers with `ticker` on `start()`, and calls `renderer.render(stage, buffer, matrix)` every tick. One `Player` per app — see §2. | `display/Stage.ts`, `player/Player.ts` |
|
|
143
|
+
| Logical size vs render resolution | `ScreenAdapter` keeps Stage and touch coordinates in logical units while sizing the Canvas backing store independently from its CSS size. `KurotOptions.resolution` defaults to `min(devicePixelRatio, 2)` and `ScreenAdapter.resolution` can change it at runtime. `Player` supplies the logical-to-physical root matrix; text, automatic display-list caches, and effect buffers inherit the resulting renderer resolution unless explicitly overridden. | `player/ScreenAdapter.ts`, `player/Player.ts`, `player/pipes/TextPipe.ts`, `player/canvas/DisplayList.ts` |
|
|
144
|
+
| GPU texture GC | `GraphicsPipe`/`TextPipe` register cached textures through `RenderContext.registerTextureForGC()`. The WebGL context owns the module-level `FinalizationRegistry`; explicit destruction unregisters the token and deletes the texture immediately. | `player/RenderContext.ts`, `player/pipes/GraphicsPipe.ts`, `player/pipes/TextPipe.ts`, `player/webgl/WebGLRenderContext.ts` |
|
|
145
|
+
|
|
146
|
+
## 4. Public API surface (`src/index.ts`)
|
|
147
|
+
|
|
148
|
+
Re-export order: `events`, `geom`, `utils`, `display`, `net`, `filters`,
|
|
149
|
+
`media`, `player`, `text`, `system`, then `localStorage` (namespaced),
|
|
150
|
+
`ExternalInterface` (named), `resource`.
|
|
151
|
+
|
|
152
|
+
- **Events**: `Event`, `EventMap` (type), `EventPhase`, `IEventDispatcher`, `EventDispatcher`, `FocusEvent`, `HTTPStatusEvent`, `IOErrorEvent`, `ProgressEvent`, `StageOrientationEvent`, `TextEvent`, `TimerEvent`, `TouchEvent`.
|
|
153
|
+
- **Geom**: `Point`/`sharedPoint`, `Rectangle`/`sharedRectangle`, `Matrix`/`sharedMatrix`.
|
|
154
|
+
- **Utils**: `NumberUtils`, `Base64Util`, `toColorString`, `Logger`/`LogLevel`, `Timer`/`TimerEvents`, `ByteArray`/`Endian`, `registerFontMapping`/`cacheFontResource`, `DebugLog`.
|
|
155
|
+
- **Display**: enums (`BitmapFillMode`, `BlendMode`/`blendModeToNumber`/`numberToBlendMode`, `CapsStyle`, `GradientType`, `JointStyle`, `OrientationMode`, `StageScaleMode`); `DisplayObject`/`RenderMode`/`RenderObjectType`/`DisplayObjectEvents`/`CacheAsTextureOptions`; `DisplayObjectContainer`, `Stage`, `Graphics`/`setGraphicsHitTest`, `Shape`, `Sprite`, `Bitmap`/`setBitmapPixelHitTest`, `Mesh`; textures: `BitmapData`/`CompressedTextureData`, `Texture`/`textureScaleFactor`, `RenderTexture`, `SpriteSheet`; `PathCommandType`/`GraphicsCommand` (type).
|
|
156
|
+
- **Net**: `HttpMethod`/`HttpMethodType`, `HttpResponseType`/`HttpResponseTypeType`, `HttpRequest`/`HttpRequestEvents`, `ImageLoader`/`ImageLoaderEvents`.
|
|
157
|
+
- **Filters**: `Filter`, `BlurFilter`, `ColorMatrixFilter`, `GlowFilter`, `DropShadowFilter`, `CustomFilter`, `MultiPassFilter`, `BloomFilter`; types `CustomFilterOptions`, `CustomFilterUniform`, `FilterProgramSource`, `FilterTexture`, `FilterPass`, `FilterPassInput`, `BloomFilterOptions`.
|
|
158
|
+
- **Media**: `Sound`/`SoundType`/`SoundEvents`, `SoundChannel`, `Video`.
|
|
159
|
+
- **Player**: `Player`, `createPlayer`/`KurotApp`/`KurotOptions`; ticker: `SystemTicker`, `ticker`, `getTimer`, `setupLifecycle`, `START_TIME`, `invalidateRenderFlag`/`setInvalidateRenderFlag`, `requestRenderingFlag`/`setRequestRenderingFlag`, `Renderable`; rendering: `InstructionSet`/`Instruction`, `RenderPipe` (type), `CanvasBuffer`, `hitTestBuffer`, `CanvasRenderer`, `DisplayList`; input/layout: `TouchHandler`, `ScreenAdapter`/`StageDisplaySize`; WebGL: `WebGLRenderer`, `WebGLRenderContext`, `WebGLRenderBuffer`, `WebGLRenderTarget`, `WebGLVertexArrayObject`, `WebGLDrawCmdManager`, `WebGLProgram`, `ShaderLib`, `checkWebGLSupport`, `MultiTextureBatcher`.
|
|
160
|
+
- `RenderContext` / `RenderBuffer` (`player/RenderContext.ts`, `player/RenderBuffer.ts`) are internal backend-neutral contracts and are not re-exported.
|
|
161
|
+
- **Text**: `HorizontalAlign`, `VerticalAlign`, `TextFieldType`, `TextFieldInputType`; types `ITextStyle`, `ITextElement`, `IWTextElement`, `ILineElement`, `IHitTextElement`; `HtmlTextParser`, `BitmapFont`, `BitmapText`, `measureText`/`getFontString`, `TextField`, `StageText`, `InputController`, `tokenize`/`splitGraphemes`.
|
|
162
|
+
- **System**: `Capabilities`.
|
|
163
|
+
- **localStorage**: namespace object — `import { localStorage } from '@kurot/core'`, then `localStorage.getItem(...)`.
|
|
164
|
+
- **External**: `ExternalInterface` (named, not namespaced).
|
|
165
|
+
- **Resource**: `Resource`/`resource` (shared instance), `ProgressCallback`, `ResourceEventListener`, `ResourceItem`, `ResourceType`, `ResourceConfig`/`ResourceConfigData`/`ResourceConfigEntry`, `ResourceLoader`, `ResourceEventType`/`ResourceEvent`, `AnalyzerBase`, `ImageAnalyzer`, `JsonAnalyzer`, `TextAnalyzer`, `SoundAnalyzer`, `SheetAnalyzer`.
|
|
166
|
+
|
|
167
|
+
`examples/benchmark/` is dev-only tooling and is **not** exported from `index.ts`.
|
|
168
|
+
|
|
169
|
+
## 5. Current API constraints
|
|
170
|
+
|
|
171
|
+
- Object identity uses object references; there is no `hashCode` API.
|
|
172
|
+
- Import the resource singleton as `resource`; `Resource` can also be
|
|
173
|
+
instantiated directly.
|
|
174
|
+
- One `Player` owns the runtime hooks for a page. Constructing another player
|
|
175
|
+
replaces those static hooks.
|
|
176
|
+
- `Player` constructs its own `WebGLRenderContext`; there is no context
|
|
177
|
+
singleton.
|
|
178
|
+
- `$`-prefixed members are engine internals even when TypeScript visibility
|
|
179
|
+
permits package-level access.
|
|
180
|
+
- `Base64Util.encode()` accepts `ArrayBuffer`.
|
|
181
|
+
- Pixel reads are provided by `RenderTexture.getPixel32()`; the corresponding
|
|
182
|
+
base `Texture` read/export methods throw unsupported-operation errors.
|
|
183
|
+
- Hit-test and lookup misses return `undefined`.
|
|
184
|
+
- The supported runtime target is the browser; there is no native-wrapper
|
|
185
|
+
backend.
|
|
186
|
+
- Event sources can specialize `EventDispatcher<EventMap>` for typed listener
|
|
187
|
+
callbacks; untyped callers use the base `Event` overload.
|
|
188
|
+
- `MultiTextureBatcher.MAX_TEXTURES` is currently fixed at 8.
|
|
189
|
+
|
|
190
|
+
## 6. Task → file map
|
|
191
|
+
|
|
192
|
+
| I want to... | Look at |
|
|
193
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| Add a new DisplayObject subclass | `display/DisplayObject.ts`, model it on `display/Shape.ts` |
|
|
195
|
+
| Add a new filter | `docs/filters.md`, `filters/CustomFilter.ts`, `filters/MultiPassFilter.ts`; execution in `player/webgl/WebGLFilterSystem.ts`, scene integration in `player/pipes/FilterPipe.ts` |
|
|
196
|
+
| Change how instructions are built/executed | `player/webgl/WebGLRenderer.ts`, `player/InstructionSet.ts` |
|
|
197
|
+
| Add a new resource type/parser | `resource/analyzers/`, register in `Resource.ts` |
|
|
198
|
+
| Debug a texture-batching issue | `player/webgl/MultiTextureBatcher.ts`, `player/webgl/WebGLDrawCmdManager.ts` |
|
|
199
|
+
| Change text layout/wrapping | `text/LineBreaks.ts`, `text/TextLineLayout.ts`, `text/TextMeasurer.ts`; `docs/text-layout.md` |
|
|
200
|
+
| Understand dirty-flag propagation | `display/DisplayObject.ts` (`$markDirty`, `$cacheDirtyUp`, `$renderDirtyUp`) |
|
|
201
|
+
| Run perf tests | `examples/benchmark/`, `pnpm benchmark`; automated Kurot/PixiJS/Egret comparison via `pnpm benchmark:compare` |
|
|
202
|
+
|
|
203
|
+
### Mesh geometry and batching
|
|
204
|
+
|
|
205
|
+
Mesh vertices are local positions; texture trim offsets do not shift geometry.
|
|
206
|
+
UVs are relative to the unrotated texture region; both backends map atlas
|
|
207
|
+
rotation during sampling. Resize geometry using scaleX/scaleY. After mutating
|
|
208
|
+
vertices, UVs, or indices, call updateVertices(). WebGL keeps Mesh indices
|
|
209
|
+
separate from quad indices and splits oversized meshes into ordered batches
|
|
210
|
+
with local index remapping (player/webgl/split-mesh.ts).
|
|
211
|
+
|
|
212
|
+
## Text layout in 2.0.1
|
|
213
|
+
|
|
214
|
+
Word-wrapped text uses Unicode 17.0 UAX #14, dictionary tailoring for SA scripts
|
|
215
|
+
and alphabetic overflow tailoring. Style runs never introduce break positions.
|
|
216
|
+
Automatic-wrap spaces count in source offsets but not painted width. Core's
|
|
217
|
+
Canvas renderer advances input indices by line.charNum, including hidden spaces
|
|
218
|
+
and CRLF; do not derive the next line's source offset from painted text lengths.
|
|
219
|
+
See [text layout](text-layout.md).
|
|
220
|
+
|
|
221
|
+
## Resource configuration change in 2.0.0
|
|
222
|
+
|
|
223
|
+
`ResourceConfigEntry.subkeys` is an object map, and Core rejects legacy strings
|
|
224
|
+
before mutating the resource registry. See
|
|
225
|
+
[the resource-default contract](../../ui-document/docs/resource-nine-slice.md)
|
|
226
|
+
for conversion and compilation boundaries.
|
|
227
|
+
|
|
228
|
+
## Text layout contract in 2.1.0
|
|
229
|
+
|
|
230
|
+
Explicit `TextField.multiline = false` renders the first hard-separated line
|
|
231
|
+
without width wrapping. Dynamic fields default to multiline and input fields
|
|
232
|
+
to single-line until assigned; explicit intent survives type changes.
|
|
233
|
+
`TextField.invalidateTextMetrics()` clears line measurements after a late-loaded
|
|
234
|
+
font becomes ready. Core does not implement Label font shrinking; that is a
|
|
235
|
+
separate UI capability requiring Core 2.1.0 or later.
|
|
236
|
+
See [text layout](text-layout.md) for the Core contract and
|
|
237
|
+
[Label text layout](../../ui/docs/label-text-layout.md) for fit bounds, state
|
|
238
|
+
restoration and shared-schema boundaries.
|