three-text 0.2.13 → 0.2.15

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.
@@ -31,12 +31,24 @@ function deepEqual(a, b) {
31
31
  return false;
32
32
  if (typeof a !== 'object' || typeof b !== 'object')
33
33
  return false;
34
+ // Arrays (common in options like color, byCharRange, etc.)
35
+ if (Array.isArray(a) || Array.isArray(b)) {
36
+ if (!Array.isArray(a) || !Array.isArray(b))
37
+ return false;
38
+ if (a.length !== b.length)
39
+ return false;
40
+ for (let i = 0; i < a.length; i++) {
41
+ if (!deepEqual(a[i], b[i]))
42
+ return false;
43
+ }
44
+ return true;
45
+ }
34
46
  const keysA = Object.keys(a);
35
47
  const keysB = Object.keys(b);
36
48
  if (keysA.length !== keysB.length)
37
49
  return false;
38
50
  for (const key of keysA) {
39
- if (!keysB.includes(key))
51
+ if (!Object.prototype.hasOwnProperty.call(b, key))
40
52
  return false;
41
53
  if (!deepEqual(a[key], b[key]))
42
54
  return false;
@@ -54,10 +66,14 @@ const Text$1 = react.forwardRef(function Text(props, ref) {
54
66
  const { children, font, material, position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1], onLoad, onError, vertexColors = true, ...restOptions } = props;
55
67
  const [geometry, setGeometry] = react.useState(null);
56
68
  const [error, setError] = react.useState(null);
57
- const defaultMaterial = react.useMemo(() => new THREE__namespace.MeshBasicMaterial({
58
- color: 0xffffff,
59
- side: THREE__namespace.DoubleSide,
60
- }), []);
69
+ const geometryRef = react.useRef(null);
70
+ const defaultMaterial = react.useMemo(() => {
71
+ return new THREE__namespace.MeshBasicMaterial({
72
+ color: 0xffffff,
73
+ side: THREE__namespace.DoubleSide,
74
+ vertexColors
75
+ });
76
+ }, [vertexColors]);
61
77
  const finalMaterial = material || defaultMaterial;
62
78
  const memoizedTextOptions = useDeepCompareMemo(restOptions);
63
79
  react.useEffect(() => {
@@ -70,13 +86,19 @@ const Text$1 = react.forwardRef(function Text(props, ref) {
70
86
  const text = await index.Text.create({
71
87
  text: children,
72
88
  font,
73
- ...memoizedTextOptions,
89
+ ...memoizedTextOptions
74
90
  });
75
- if (!cancelled) {
76
- setGeometry(text.geometry);
77
- if (onLoad)
78
- onLoad(text.geometry, text);
91
+ if (cancelled) {
92
+ // If a newer render superseded this request, avoid leaking geometry
93
+ text.geometry.dispose();
94
+ return;
79
95
  }
96
+ // Dispose previous geometry (if any) before swapping
97
+ geometryRef.current?.dispose();
98
+ geometryRef.current = text.geometry;
99
+ setGeometry(text.geometry);
100
+ if (onLoad)
101
+ onLoad(text.geometry, text);
80
102
  }
81
103
  catch (err) {
82
104
  const error = err;
@@ -85,7 +107,7 @@ const Text$1 = react.forwardRef(function Text(props, ref) {
85
107
  if (onError)
86
108
  onError(error);
87
109
  else
88
- console.error("ThreeText error:", error);
110
+ console.error('ThreeText error:', error);
89
111
  }
90
112
  }
91
113
  }
@@ -93,18 +115,20 @@ const Text$1 = react.forwardRef(function Text(props, ref) {
93
115
  return () => {
94
116
  cancelled = true;
95
117
  };
96
- }, [font, children, memoizedTextOptions, onLoad, onError, vertexColors]);
97
- // Handle geometry and material cleanup
118
+ }, [font, children, memoizedTextOptions, onLoad, onError]);
119
+ // Cleanup geometry on unmount
98
120
  react.useEffect(() => {
99
121
  return () => {
100
- if (geometry) {
101
- geometry.dispose();
102
- }
103
- if (!material && defaultMaterial) {
104
- defaultMaterial.dispose();
105
- }
122
+ geometryRef.current?.dispose();
123
+ geometryRef.current = null;
124
+ };
125
+ }, []);
126
+ // Cleanup default material when it changes or on unmount
127
+ react.useEffect(() => {
128
+ return () => {
129
+ defaultMaterial.dispose();
106
130
  };
107
- }, [geometry, material, defaultMaterial]);
131
+ }, [defaultMaterial]);
108
132
  if (error || !geometry) {
109
133
  return null;
110
134
  }
@@ -2,6 +2,13 @@ import * as react from 'react';
2
2
  import * as THREE from 'three';
3
3
  import { TextOptions as TextOptions$1, ThreeTextGeometryInfo, Text as Text$2 } from './index';
4
4
 
5
+ interface HyphenationTrieNode {
6
+ patterns: number[] | null;
7
+ children: {
8
+ [char: string]: HyphenationTrieNode;
9
+ };
10
+ }
11
+
5
12
  interface BoundingBox {
6
13
  min: {
7
14
  x: number;
@@ -15,13 +22,6 @@ interface BoundingBox {
15
22
  };
16
23
  }
17
24
 
18
- interface HyphenationTrieNode {
19
- patterns: number[] | null;
20
- children: {
21
- [char: string]: HyphenationTrieNode;
22
- };
23
- }
24
-
25
25
  type TextAlign = 'left' | 'center' | 'right' | 'justify';
26
26
  type TextDirection = 'ltr' | 'rtl';
27
27
  interface LoadedFont {
@@ -187,6 +187,13 @@ interface TextGeometryInfo {
187
187
  query(options: TextQueryOptions): TextRange[];
188
188
  coloredRanges?: ColoredRange[];
189
189
  }
190
+ interface TextHandle extends TextGeometryInfo {
191
+ getLoadedFont(): LoadedFont | undefined;
192
+ getCacheStatistics(): any;
193
+ clearCache(): void;
194
+ measureTextWidth(text: string, letterSpacing?: number): number;
195
+ update(options: Partial<TextOptions>): Promise<TextHandle>;
196
+ }
190
197
  interface ColorByRange {
191
198
  start: number;
192
199
  end: number;
@@ -279,16 +286,6 @@ interface LayoutOptions {
279
286
  shortLineThreshold?: number;
280
287
  }
281
288
 
282
- interface GlyphCacheStats {
283
- hits: number;
284
- misses: number;
285
- totalGlyphs: number;
286
- uniqueGlyphs: number;
287
- cacheSize: number;
288
- saved: number;
289
- memoryUsage: number;
290
- }
291
-
292
289
  declare global {
293
290
  interface Window {
294
291
  hbjs?: any;
@@ -299,7 +296,10 @@ declare class Text$1 {
299
296
  private static patternCache;
300
297
  private static hbInitPromise;
301
298
  private static fontCache;
299
+ private static fontCacheMemoryBytes;
300
+ private static maxFontCacheMemoryBytes;
302
301
  private static fontIdCounter;
302
+ private static stableStringify;
303
303
  private fontLoader;
304
304
  private loadedFont?;
305
305
  private currentFontId;
@@ -310,13 +310,12 @@ declare class Text$1 {
310
310
  static setHarfBuzzPath(path: string): void;
311
311
  static setHarfBuzzBuffer(wasmBuffer: ArrayBuffer): void;
312
312
  static init(): Promise<HarfBuzzInstance>;
313
- static create(options: TextOptions): Promise<TextGeometryInfo & Pick<Text$1, 'getLoadedFont' | 'getCacheStatistics' | 'clearCache' | 'measureTextWidth'> & {
314
- update: (options: Partial<TextOptions>) => Promise<TextGeometryInfo & Pick<Text$1, 'getLoadedFont' | 'getCacheStatistics' | 'clearCache' | 'measureTextWidth'> & {
315
- update: (options: Partial<TextOptions>) => Promise<any>;
316
- }>;
317
- }>;
313
+ static create(options: TextOptions): Promise<TextHandle>;
318
314
  private static resolveFont;
319
315
  private static loadAndCacheFont;
316
+ private static trackFontCacheAdd;
317
+ private static trackFontCacheRemove;
318
+ private static enforceFontCacheMemoryLimit;
320
319
  private static generateFontContentHash;
321
320
  private setLoadedFont;
322
321
  private loadFont;
@@ -330,16 +329,29 @@ declare class Text$1 {
330
329
  getFontMetrics(): FontMetrics;
331
330
  static preloadPatterns(languages: string[], patternsPath?: string): Promise<void>;
332
331
  static registerPattern(language: string, pattern: HyphenationTrieNode): void;
332
+ static clearFontCache(): void;
333
+ static setMaxFontCacheMemoryMB(limitMB: number): void;
333
334
  getLoadedFont(): LoadedFont | undefined;
334
335
  measureTextWidth(text: string, letterSpacing?: number): number;
335
- getCacheStatistics(): GlyphCacheStats | null;
336
+ getCacheStatistics(): (CacheStats & {
337
+ hitRate: number;
338
+ memoryUsageMB: number;
339
+ }) | null;
336
340
  clearCache(): void;
337
341
  private createGlyphAttributes;
338
342
  private resetHelpers;
339
343
  destroy(): void;
340
344
  }
341
345
 
342
- interface ThreeTextProps extends Omit<TextOptions$1, "text"> {
346
+ interface CacheStats {
347
+ hits: number;
348
+ misses: number;
349
+ evictions: number;
350
+ size: number;
351
+ memoryUsage: number;
352
+ }
353
+
354
+ interface ThreeTextProps extends Omit<TextOptions$1, 'text'> {
343
355
  children: string;
344
356
  font: string | ArrayBuffer;
345
357
  material?: THREE.Material;
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { forwardRef, useState, useMemo, useEffect, useRef } from 'react';
2
+ import { forwardRef, useState, useRef, useMemo, useEffect } from 'react';
3
3
  import * as THREE from 'three';
4
4
  import { Text as Text$2 } from './index';
5
5
 
@@ -10,12 +10,24 @@ function deepEqual(a, b) {
10
10
  return false;
11
11
  if (typeof a !== 'object' || typeof b !== 'object')
12
12
  return false;
13
+ // Arrays (common in options like color, byCharRange, etc.)
14
+ if (Array.isArray(a) || Array.isArray(b)) {
15
+ if (!Array.isArray(a) || !Array.isArray(b))
16
+ return false;
17
+ if (a.length !== b.length)
18
+ return false;
19
+ for (let i = 0; i < a.length; i++) {
20
+ if (!deepEqual(a[i], b[i]))
21
+ return false;
22
+ }
23
+ return true;
24
+ }
13
25
  const keysA = Object.keys(a);
14
26
  const keysB = Object.keys(b);
15
27
  if (keysA.length !== keysB.length)
16
28
  return false;
17
29
  for (const key of keysA) {
18
- if (!keysB.includes(key))
30
+ if (!Object.prototype.hasOwnProperty.call(b, key))
19
31
  return false;
20
32
  if (!deepEqual(a[key], b[key]))
21
33
  return false;
@@ -33,10 +45,14 @@ const Text$1 = forwardRef(function Text(props, ref) {
33
45
  const { children, font, material, position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1], onLoad, onError, vertexColors = true, ...restOptions } = props;
34
46
  const [geometry, setGeometry] = useState(null);
35
47
  const [error, setError] = useState(null);
36
- const defaultMaterial = useMemo(() => new THREE.MeshBasicMaterial({
37
- color: 0xffffff,
38
- side: THREE.DoubleSide,
39
- }), []);
48
+ const geometryRef = useRef(null);
49
+ const defaultMaterial = useMemo(() => {
50
+ return new THREE.MeshBasicMaterial({
51
+ color: 0xffffff,
52
+ side: THREE.DoubleSide,
53
+ vertexColors
54
+ });
55
+ }, [vertexColors]);
40
56
  const finalMaterial = material || defaultMaterial;
41
57
  const memoizedTextOptions = useDeepCompareMemo(restOptions);
42
58
  useEffect(() => {
@@ -49,13 +65,19 @@ const Text$1 = forwardRef(function Text(props, ref) {
49
65
  const text = await Text$2.create({
50
66
  text: children,
51
67
  font,
52
- ...memoizedTextOptions,
68
+ ...memoizedTextOptions
53
69
  });
54
- if (!cancelled) {
55
- setGeometry(text.geometry);
56
- if (onLoad)
57
- onLoad(text.geometry, text);
70
+ if (cancelled) {
71
+ // If a newer render superseded this request, avoid leaking geometry
72
+ text.geometry.dispose();
73
+ return;
58
74
  }
75
+ // Dispose previous geometry (if any) before swapping
76
+ geometryRef.current?.dispose();
77
+ geometryRef.current = text.geometry;
78
+ setGeometry(text.geometry);
79
+ if (onLoad)
80
+ onLoad(text.geometry, text);
59
81
  }
60
82
  catch (err) {
61
83
  const error = err;
@@ -64,7 +86,7 @@ const Text$1 = forwardRef(function Text(props, ref) {
64
86
  if (onError)
65
87
  onError(error);
66
88
  else
67
- console.error("ThreeText error:", error);
89
+ console.error('ThreeText error:', error);
68
90
  }
69
91
  }
70
92
  }
@@ -72,18 +94,20 @@ const Text$1 = forwardRef(function Text(props, ref) {
72
94
  return () => {
73
95
  cancelled = true;
74
96
  };
75
- }, [font, children, memoizedTextOptions, onLoad, onError, vertexColors]);
76
- // Handle geometry and material cleanup
97
+ }, [font, children, memoizedTextOptions, onLoad, onError]);
98
+ // Cleanup geometry on unmount
77
99
  useEffect(() => {
78
100
  return () => {
79
- if (geometry) {
80
- geometry.dispose();
81
- }
82
- if (!material && defaultMaterial) {
83
- defaultMaterial.dispose();
84
- }
101
+ geometryRef.current?.dispose();
102
+ geometryRef.current = null;
103
+ };
104
+ }, []);
105
+ // Cleanup default material when it changes or on unmount
106
+ useEffect(() => {
107
+ return () => {
108
+ defaultMaterial.dispose();
85
109
  };
86
- }, [geometry, material, defaultMaterial]);
110
+ }, [defaultMaterial]);
87
111
  if (error || !geometry) {
88
112
  return null;
89
113
  }
@@ -1,4 +1,4 @@
1
- import type { TextOptions, TextGeometryInfo, FontMetrics, LoadedFont, HarfBuzzInstance } from './types';
1
+ import type { TextOptions, TextHandle, FontMetrics, LoadedFont, HarfBuzzInstance } from './types';
2
2
  import type { HyphenationTrieNode } from '../hyphenation';
3
3
  declare global {
4
4
  interface Window {
@@ -10,7 +10,10 @@ export declare class Text {
10
10
  private static patternCache;
11
11
  private static hbInitPromise;
12
12
  private static fontCache;
13
+ private static fontCacheMemoryBytes;
14
+ private static maxFontCacheMemoryBytes;
13
15
  private static fontIdCounter;
16
+ private static stableStringify;
14
17
  private fontLoader;
15
18
  private loadedFont?;
16
19
  private currentFontId;
@@ -21,13 +24,12 @@ export declare class Text {
21
24
  static setHarfBuzzPath(path: string): void;
22
25
  static setHarfBuzzBuffer(wasmBuffer: ArrayBuffer): void;
23
26
  static init(): Promise<HarfBuzzInstance>;
24
- static create(options: TextOptions): Promise<TextGeometryInfo & Pick<Text, 'getLoadedFont' | 'getCacheStatistics' | 'clearCache' | 'measureTextWidth'> & {
25
- update: (options: Partial<TextOptions>) => Promise<TextGeometryInfo & Pick<Text, 'getLoadedFont' | 'getCacheStatistics' | 'clearCache' | 'measureTextWidth'> & {
26
- update: (options: Partial<TextOptions>) => Promise<any>;
27
- }>;
28
- }>;
27
+ static create(options: TextOptions): Promise<TextHandle>;
29
28
  private static resolveFont;
30
29
  private static loadAndCacheFont;
30
+ private static trackFontCacheAdd;
31
+ private static trackFontCacheRemove;
32
+ private static enforceFontCacheMemoryLimit;
31
33
  private static generateFontContentHash;
32
34
  private setLoadedFont;
33
35
  private loadFont;
@@ -41,9 +43,14 @@ export declare class Text {
41
43
  getFontMetrics(): FontMetrics;
42
44
  static preloadPatterns(languages: string[], patternsPath?: string): Promise<void>;
43
45
  static registerPattern(language: string, pattern: HyphenationTrieNode): void;
46
+ static clearFontCache(): void;
47
+ static setMaxFontCacheMemoryMB(limitMB: number): void;
44
48
  getLoadedFont(): LoadedFont | undefined;
45
49
  measureTextWidth(text: string, letterSpacing?: number): number;
46
- getCacheStatistics(): import("./cache/GlyphCache").GlyphCacheStats | null;
50
+ getCacheStatistics(): (import("..").CacheStats & {
51
+ hitRate: number;
52
+ memoryUsageMB: number;
53
+ }) | null;
47
54
  clearCache(): void;
48
55
  private createGlyphAttributes;
49
56
  private resetHelpers;
@@ -1,6 +1,6 @@
1
- import { GlyphCache } from './GlyphCache';
2
- import { GlyphGeometryInfo, LoadedFont, GlyphCluster } from '../types';
1
+ import { GlyphGeometryInfo, LoadedFont, GlyphCluster, GlyphData } from '../types';
3
2
  import { CurveFidelityConfig, GeometryOptimizationOptions } from '../types';
3
+ import { LRUCache } from '../../utils/LRUCache';
4
4
  export interface InstancedTextGeometry {
5
5
  vertices: Float32Array;
6
6
  normals: Float32Array;
@@ -24,6 +24,9 @@ export declare class GlyphGeometryBuilder {
24
24
  private tessellator;
25
25
  private extruder;
26
26
  private fontId;
27
+ private cacheKeyPrefix;
28
+ private curveFidelityConfig?;
29
+ private geometryOptimizationOptions?;
27
30
  private clusterer;
28
31
  private collector;
29
32
  private drawCallbacks;
@@ -31,20 +34,24 @@ export declare class GlyphGeometryBuilder {
31
34
  private wordCache;
32
35
  private contourCache;
33
36
  private clusteringCache;
34
- constructor(cache: GlyphCache, loadedFont: LoadedFont);
37
+ constructor(cache: LRUCache<string, GlyphData>, loadedFont: LoadedFont);
35
38
  getOptimizationStats(): import("../geometry/PathOptimizer").OptimizationStats;
36
39
  setCurveFidelityConfig(config?: CurveFidelityConfig): void;
37
40
  setGeometryOptimization(options?: GeometryOptimizationOptions): void;
38
41
  setFontId(fontId: string): void;
42
+ private updateCacheKeyPrefix;
43
+ private getGeometryConfigSignature;
39
44
  buildInstancedGeometry(clustersByLine: GlyphCluster[][], depth: number, removeOverlaps: boolean, isCFF: boolean, separateGlyphs?: boolean, coloredTextIndices?: Set<number>): InstancedTextGeometry;
40
45
  private getClusterKey;
41
- private appendGeometry;
42
46
  private createGlyphInfo;
43
47
  private getContoursForGlyph;
44
48
  private tessellateGlyphCluster;
45
49
  private extrudeAndPackage;
46
50
  private tessellateGlyph;
47
51
  private updatePlaneBounds;
48
- getCacheStats(): import("./GlyphCache").GlyphCacheStats;
52
+ getCacheStats(): import("../../utils/LRUCache").CacheStats & {
53
+ hitRate: number;
54
+ memoryUsageMB: number;
55
+ };
49
56
  clearCache(): void;
50
57
  }
@@ -0,0 +1,12 @@
1
+ import { LRUCache } from '../../utils/LRUCache';
2
+ import type { GlyphData, GlyphContours } from '../types';
3
+ export declare function getGlyphCacheKey(fontId: string, glyphId: number, depth: number, removeOverlaps: boolean): string;
4
+ export declare function calculateGlyphMemoryUsage(glyph: GlyphData): number;
5
+ export declare const globalGlyphCache: LRUCache<string, GlyphData>;
6
+ export declare function createGlyphCache(maxCacheSizeMB?: number): LRUCache<string, GlyphData>;
7
+ export declare const globalContourCache: LRUCache<string, GlyphContours>;
8
+ export declare const globalWordCache: LRUCache<string, GlyphData>;
9
+ export declare const globalClusteringCache: LRUCache<string, {
10
+ glyphIds: number[];
11
+ groups: number[][];
12
+ }>;
@@ -0,0 +1,7 @@
1
+ export interface TableDirectoryEntry {
2
+ tag: number;
3
+ checksum: number;
4
+ offset: number;
5
+ length: number;
6
+ }
7
+ export declare function parseTableDirectory(view: DataView): Map<number, TableDirectoryEntry>;
@@ -1,6 +1,5 @@
1
1
  export declare const FONT_SIGNATURE_TRUE_TYPE = 65536;
2
2
  export declare const FONT_SIGNATURE_OPEN_TYPE_CFF = 1330926671;
3
- export declare const FONT_SIGNATURE_TRUE_TYPE_COLLECTION = 1953784678;
4
3
  export declare const FONT_SIGNATURE_WOFF = 2001684038;
5
4
  export declare const FONT_SIGNATURE_WOFF2 = 2001684018;
6
5
  export declare const TABLE_TAG_HEAD = 1751474532;
@@ -1,13 +1,10 @@
1
1
  import type { ProcessedGeometry } from '../types';
2
2
  export interface ExtrusionResult {
3
- vertices: number[];
4
- normals: number[];
5
- indices: number[];
3
+ vertices: Float32Array;
4
+ normals: Float32Array;
5
+ indices: Uint32Array;
6
6
  }
7
7
  export declare class Extruder {
8
8
  constructor();
9
9
  extrude(geometry: ProcessedGeometry, depth: number | undefined, unitsPerEm: number): ExtrusionResult;
10
- private addFlatFaces;
11
- private addFrontAndBackFaces;
12
- private addSideWalls;
13
10
  }
@@ -50,6 +50,7 @@ export interface LineBreakOptions {
50
50
  respectExistingBreaks?: boolean;
51
51
  hyphenationPatterns?: HyphenationPatternsMap;
52
52
  unitsPerEm?: number;
53
+ letterSpacing?: number;
53
54
  tolerance?: number;
54
55
  pretolerance?: number;
55
56
  emergencyStretch?: number;
@@ -73,6 +74,7 @@ interface LineBreakContext {
73
74
  exHyphenPenalty: number;
74
75
  currentAlign: TextAlign;
75
76
  unitsPerEm?: number;
77
+ letterSpacingFU?: number;
76
78
  }
77
79
  export declare class LineBreak {
78
80
  private static badness;
@@ -11,7 +11,9 @@ export declare class DrawCallbackHandler {
11
11
  private position;
12
12
  setPosition(x: number, y: number): void;
13
13
  updatePosition(dx: number, dy: number): void;
14
+ setCollector(collector: GlyphContourCollector): void;
14
15
  createDrawFuncs(font: LoadedFont, collector: GlyphContourCollector): void;
15
16
  getDrawFuncsPtr(): number;
16
17
  destroy(font: LoadedFont): void;
17
18
  }
19
+ export declare function getSharedDrawCallbackHandler(font: LoadedFont): DrawCallbackHandler;
@@ -31,5 +31,8 @@ export declare class TextShaper {
31
31
  private calculateSpaceAdjustment;
32
32
  private calculateCJKAdjustment;
33
33
  clearCache(): void;
34
- getCacheStats(): import("../..").GlyphCacheStats;
34
+ getCacheStats(): import("../..").CacheStats & {
35
+ hitRate: number;
36
+ memoryUsageMB: number;
37
+ };
35
38
  }
@@ -163,6 +163,25 @@ export interface ProcessedGeometry {
163
163
  triangles: Triangles;
164
164
  contours: number[][];
165
165
  }
166
+ export interface GlyphData {
167
+ geometry: ProcessedGeometry;
168
+ vertices: Float32Array;
169
+ normals: Float32Array;
170
+ indices: Uint32Array;
171
+ bounds: {
172
+ min: {
173
+ x: number;
174
+ y: number;
175
+ z: number;
176
+ };
177
+ max: {
178
+ x: number;
179
+ y: number;
180
+ z: number;
181
+ };
182
+ };
183
+ useCount: number;
184
+ }
166
185
  export interface PathInfo {
167
186
  start: number;
168
187
  count: number;
@@ -234,6 +253,13 @@ export interface TextGeometryInfo {
234
253
  query(options: TextQueryOptions): TextRange[];
235
254
  coloredRanges?: ColoredRange[];
236
255
  }
256
+ export interface TextHandle extends TextGeometryInfo {
257
+ getLoadedFont(): LoadedFont | undefined;
258
+ getCacheStatistics(): any;
259
+ clearCache(): void;
260
+ measureTextWidth(text: string, letterSpacing?: number): number;
261
+ update(options: Partial<TextOptions>): Promise<TextHandle>;
262
+ }
237
263
  export interface ColorByRange {
238
264
  start: number;
239
265
  end: number;
@@ -1,7 +1,7 @@
1
1
  export { Text } from './core/Text';
2
2
  export { DEFAULT_CURVE_FIDELITY } from './core/geometry/Polygonizer';
3
3
  export { FontMetadataExtractor } from './core/font/FontMetadata';
4
- export { globalGlyphCache } from './core/cache/GlyphCache';
5
- export type { GlyphCache, GlyphCacheStats, GlyphData } from './core/cache/GlyphCache';
6
- export type { TextAlign, TextDirection, LineInfo, LoadedFont, HarfBuzzModule, HarfBuzzAPI, HarfBuzzBlob, HarfBuzzFace, HarfBuzzFont, HarfBuzzBuffer, HarfBuzzInstance, VariationAxis, ExtractedMetrics, VerticalMetrics, FontMetrics, ProcessedGeometry, Triangles, GlyphGeometryInfo, TextGeometryInfo, TextOptions, ColorOptions, ColorByRange, ColoredRange, PathInfo, HyphenationPatternsMap, CurveFidelityConfig, LayoutOptions, GeometryOptimizationOptions, TextRange, TextQueryOptions } from './core/types';
4
+ export { globalGlyphCache, createGlyphCache } from './core/cache/sharedCaches';
5
+ export type { CacheStats } from './utils/LRUCache';
6
+ export type { TextAlign, TextDirection, LineInfo, LoadedFont, HarfBuzzModule, HarfBuzzAPI, HarfBuzzBlob, HarfBuzzFace, HarfBuzzFont, HarfBuzzBuffer, HarfBuzzInstance, VariationAxis, ExtractedMetrics, VerticalMetrics, FontMetrics, ProcessedGeometry, Triangles, GlyphData, GlyphGeometryInfo, TextGeometryInfo, TextHandle, TextOptions, ColorOptions, ColorByRange, ColoredRange, PathInfo, HyphenationPatternsMap, CurveFidelityConfig, LayoutOptions, GeometryOptimizationOptions, TextRange, TextQueryOptions } from './core/types';
7
7
  export type { HyphenationTrieNode } from './hyphenation';
@@ -1,6 +1,6 @@
1
- import * as THREE from "three";
2
- import type { TextOptions, ThreeTextGeometryInfo as TextGeometryInfo } from "./index";
3
- export interface ThreeTextProps extends Omit<TextOptions, "text"> {
1
+ import * as THREE from 'three';
2
+ import type { TextOptions, ThreeTextGeometryInfo as TextGeometryInfo } from './index';
3
+ export interface ThreeTextProps extends Omit<TextOptions, 'text'> {
4
4
  children: string;
5
5
  font: string | ArrayBuffer;
6
6
  material?: THREE.Material;
@@ -4,7 +4,7 @@ export interface LRUCacheOptions<K, V> {
4
4
  calculateSize?: (value: V) => number;
5
5
  onEvict?: (key: K, value: V) => void;
6
6
  }
7
- export interface LRUCacheStats {
7
+ export interface CacheStats {
8
8
  hits: number;
9
9
  misses: number;
10
10
  evictions: number;
@@ -23,7 +23,7 @@ export declare class LRUCache<K, V> {
23
23
  set(key: K, value: V): void;
24
24
  delete(key: K): boolean;
25
25
  clear(): void;
26
- getStats(): LRUCacheStats & {
26
+ getStats(): CacheStats & {
27
27
  hitRate: number;
28
28
  memoryUsageMB: number;
29
29
  };
@@ -9,8 +9,7 @@ export interface WebGPUBufferSet {
9
9
  vertex: GPUVertexBufferLayout;
10
10
  color?: GPUVertexBufferLayout;
11
11
  };
12
- indexFormat: GPUIndexFormat;
13
- vertexCount: number;
12
+ indexCount: number;
14
13
  dispose(): void;
15
14
  }
16
15
  export declare function createWebGPUBuffers(device: GPUDevice, textGeometry: TextGeometryInfo): WebGPUBufferSet;
@@ -3,7 +3,7 @@
3
3
  // WebGPU adapter - lightweight utility to create GPU buffers from core geometry
4
4
  function createWebGPUBuffers(device, textGeometry) {
5
5
  const { vertices, normals, indices, colors } = textGeometry;
6
- const vertexCount = indices.length;
6
+ const indexCount = indices.length;
7
7
  // Interleave position and normal data for better cache coherency
8
8
  // Layout: [px, py, pz, nx, ny, nz, px, py, pz, nx, ny, nz, ...]
9
9
  const interleavedData = new Float32Array((vertices.length / 3) * 6);
@@ -27,7 +27,7 @@ function createWebGPUBuffers(device, textGeometry) {
27
27
  });
28
28
  new Float32Array(vertexBuffer.getMappedRange()).set(interleavedData);
29
29
  vertexBuffer.unmap();
30
- // Create index buffer (NO FLIP - pass through)
30
+ // Create index buffer
31
31
  const indexBuffer = device.createBuffer({
32
32
  size: indices.byteLength,
33
33
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
@@ -85,8 +85,7 @@ function createWebGPUBuffers(device, textGeometry) {
85
85
  return {
86
86
  buffers,
87
87
  layout,
88
- indexFormat: 'uint32',
89
- vertexCount,
88
+ indexCount,
90
89
  dispose() {
91
90
  vertexBuffer.destroy();
92
91
  indexBuffer.destroy();
@@ -10,8 +10,7 @@ interface WebGPUBufferSet {
10
10
  vertex: GPUVertexBufferLayout;
11
11
  color?: GPUVertexBufferLayout;
12
12
  };
13
- indexFormat: GPUIndexFormat;
14
- vertexCount: number;
13
+ indexCount: number;
15
14
  dispose(): void;
16
15
  }
17
16
  declare function createWebGPUBuffers(device: GPUDevice, textGeometry: TextGeometryInfo): WebGPUBufferSet;