textmode.js 0.6.1 → 0.7.0-beta.2

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.
Files changed (27) hide show
  1. package/README.md +0 -2
  2. package/dist/textmode.esm.js +2405 -2027
  3. package/dist/textmode.umd.js +17 -9
  4. package/dist/types/index.d.ts +4 -0
  5. package/dist/types/rendering/webgl/core/Framebuffer.d.ts +2 -0
  6. package/dist/types/rendering/webgl/core/Renderer.d.ts +12 -1
  7. package/dist/types/rendering/webgl/core/Shader.d.ts +3 -0
  8. package/dist/types/rendering/webgl/utils/hash.d.ts +0 -16
  9. package/dist/types/textmode/Textmodifier.d.ts +4 -0
  10. package/dist/types/textmode/interfaces/ITextmodifier.d.ts +8 -2
  11. package/dist/types/textmode/layers/ILayerManager.d.ts +36 -0
  12. package/dist/types/textmode/layers/Layer2DCompositor.d.ts +74 -0
  13. package/dist/types/textmode/layers/LayerManager.d.ts +118 -0
  14. package/dist/types/textmode/layers/TextmodeLayer.d.ts +193 -0
  15. package/dist/types/textmode/layers/filters/FilterRegistry.d.ts +83 -0
  16. package/dist/types/textmode/layers/filters/LayerFilterManager.d.ts +74 -0
  17. package/dist/types/textmode/layers/filters/index.d.ts +3 -0
  18. package/dist/types/textmode/layers/filters/types.d.ts +123 -0
  19. package/dist/types/textmode/layers/index.d.ts +6 -0
  20. package/dist/types/textmode/layers/types.d.ts +74 -0
  21. package/dist/types/textmode/loading/LoadingScreenManager.d.ts +4 -4
  22. package/dist/types/textmode/managers/MouseManager.d.ts +6 -1
  23. package/dist/types/textmode/managers/TouchManager.d.ts +5 -0
  24. package/dist/types/textmode/mixins/interfaces/IKeyboardMixin.d.ts +0 -1
  25. package/dist/types/textmode/mixins/interfaces/IRenderingMixin.d.ts +80 -27
  26. package/dist/types/textmode/mixins/interfaces/ITouchMixin.d.ts +1 -1
  27. package/package.json +1 -1
@@ -0,0 +1,74 @@
1
+ import type { GLRenderer, GLShader, GLFramebuffer } from '../../../rendering';
2
+ import type { QueuedFilter, FilterName } from './types';
3
+ /**
4
+ * Manages filter shader compilation and application for layers.
5
+ *
6
+ * This manager:
7
+ * - Lazily compiles filter shaders on first use
8
+ * - Uses ping-pong rendering to chain multiple filters
9
+ * - Caches compiled shaders for performance
10
+ *
11
+ * @internal
12
+ */
13
+ export declare class LayerFilterManager {
14
+ private readonly _renderer;
15
+ private readonly _shaderCache;
16
+ private readonly _copyShader;
17
+ private _pingPongBuffers;
18
+ private _currentBufferIndex;
19
+ private _isInitialized;
20
+ private _filterRegistry;
21
+ /**
22
+ * Create a new LayerFilterManager.
23
+ * @param renderer The WebGL renderer instance
24
+ */
25
+ constructor(renderer: GLRenderer);
26
+ register(id: FilterName, shader: GLShader | string, uniformDefs?: Record<string, [paramName: string, defaultValue: unknown]>): Promise<void>;
27
+ unregister(id: FilterName): boolean;
28
+ /**
29
+ * Initialize ping-pong buffers for filter chain processing.
30
+ * @param width Buffer width in pixels
31
+ * @param height Buffer height in pixels
32
+ */
33
+ $initialize(width: number, height: number): void;
34
+ /**
35
+ * Apply a chain of filters to the source texture, outputting to target.
36
+ *
37
+ * @param sourceTexture The input texture (raw ASCII framebuffer)
38
+ * @param targetFramebuffer The output framebuffer (layer's ASCII framebuffer)
39
+ * @param filters The queue of filters to apply in order
40
+ * @param width Framebuffer width
41
+ * @param height Framebuffer height
42
+ */
43
+ $applyFilters(sourceTexture: WebGLTexture, targetFramebuffer: GLFramebuffer, filters: QueuedFilter[], width: number, height: number): void;
44
+ /**
45
+ * Apply a single filter pass.
46
+ */
47
+ private _applyFilter;
48
+ /**
49
+ * Get or create a cached shader for the given filter.
50
+ */
51
+ private _getOrCreateShader;
52
+ /**
53
+ * Copy a texture to a framebuffer using the copy shader.
54
+ */
55
+ private _copyTexture;
56
+ /**
57
+ * Get the next ping-pong buffer (not currently in use).
58
+ */
59
+ private _getNextBuffer;
60
+ /**
61
+ * Swap to the next ping-pong buffer.
62
+ */
63
+ private _swapBuffers;
64
+ /**
65
+ * Resize the ping-pong buffers.
66
+ * @param width New width in pixels
67
+ * @param height New height in pixels
68
+ */
69
+ $resize(width: number, height: number): void;
70
+ /**
71
+ * Dispose of all resources.
72
+ */
73
+ $dispose(): void;
74
+ }
@@ -0,0 +1,3 @@
1
+ export type { FilterName, BuiltInFilterName, BuiltInFilterParams, QueuedFilter, FilterContext, TextmodeFilterStrategy } from './types';
2
+ export { FilterRegistry } from './FilterRegistry';
3
+ export { LayerFilterManager } from './LayerFilterManager';
@@ -0,0 +1,123 @@
1
+ import type { GLShader } from '../../../rendering';
2
+ import type { GLRenderer } from '../../../rendering/webgl/core/Renderer';
3
+ /**
4
+ * Built-in filter names provided by textmode.js
5
+ */
6
+ export type BuiltInFilterName = 'invert' | 'grayscale' | 'sepia' | 'hueRotate' | 'saturate' | 'brightness' | 'contrast' | 'threshold' | 'chromaticAberration' | 'pixelate';
7
+ /**
8
+ * Filter name type that allows both built-in and custom filter names
9
+ */
10
+ export type FilterName = BuiltInFilterName | (string & {});
11
+ /**
12
+ * Filter parameter types for built-in filters.
13
+ *
14
+ * Most filters accept either a single number (for the primary parameter)
15
+ * or an object with named properties.
16
+ */
17
+ export interface BuiltInFilterParams {
18
+ /** Inverts all colors (no params needed) */
19
+ invert: void;
20
+ /** Converts to grayscale. Amount: 0-1, default 1 */
21
+ grayscale: number | {
22
+ amount?: number;
23
+ } | void;
24
+ /** Applies sepia tone. Amount: 0-1, default 1 */
25
+ sepia: number | {
26
+ amount?: number;
27
+ } | void;
28
+ /** Rotates hue. Angle in degrees */
29
+ hueRotate: number | {
30
+ angle?: number;
31
+ };
32
+ /** Adjusts saturation. Amount: 1 = normal, >1 = more saturated */
33
+ saturate: number | {
34
+ amount?: number;
35
+ };
36
+ /** Adjusts brightness. Amount: 1 = normal, >1 = brighter */
37
+ brightness: number | {
38
+ amount?: number;
39
+ };
40
+ /** Adjusts contrast. Amount: 1 = normal, >1 = more contrast */
41
+ contrast: number | {
42
+ amount?: number;
43
+ };
44
+ /** Black/white threshold. Threshold: 0-1 */
45
+ threshold: number | {
46
+ threshold?: number;
47
+ };
48
+ /** Chromatic aberration (color fringing) */
49
+ chromaticAberration: number | {
50
+ amount?: number;
51
+ direction?: [number, number];
52
+ };
53
+ /** Pixelate effect. PixelSize in pixels */
54
+ pixelate: number | {
55
+ pixelSize?: number;
56
+ };
57
+ }
58
+ /**
59
+ * A queued filter operation to be applied during rendering
60
+ * @internal
61
+ */
62
+ export interface QueuedFilter {
63
+ name: FilterName;
64
+ params: unknown;
65
+ }
66
+ /**
67
+ * Context provided to filter strategies for shader creation
68
+ */
69
+ export interface FilterContext {
70
+ /** The WebGL renderer instance */
71
+ renderer: GLRenderer;
72
+ /** The WebGL2 rendering context */
73
+ gl: WebGL2RenderingContext;
74
+ /** Width of the framebuffer being filtered */
75
+ width: number;
76
+ /** Height of the framebuffer being filtered */
77
+ height: number;
78
+ }
79
+ /**
80
+ * Interface for implementing custom filter strategies.
81
+ *
82
+ * Custom filters can be registered using {@link registerFilterStrategy} and will
83
+ * be available for use with `layer.filter('myFilter', params)`.
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * import { registerFilterStrategy } from 'textmode.js';
88
+ *
89
+ * const myCustomFilter: TextmodeFilterStrategy = {
90
+ * id: 'myFilter',
91
+ * createShader(context) {
92
+ * return context.renderer.$createShader(vertexSource, fragmentSource);
93
+ * },
94
+ * createUniforms(params, context) {
95
+ * return {
96
+ * u_intensity: params?.intensity ?? 1.0,
97
+ * u_resolution: [context.width, context.height]
98
+ * };
99
+ * }
100
+ * };
101
+ *
102
+ * registerFilterStrategy(myCustomFilter);
103
+ * ```
104
+ */
105
+ export interface TextmodeFilterStrategy {
106
+ /** Unique identifier for this filter */
107
+ readonly id: FilterName;
108
+ /**
109
+ * Create the shader program for this filter.
110
+ * Called once when the filter is first used (lazy initialization).
111
+ * @param context The filter context containing renderer and dimensions
112
+ * @returns The compiled shader program
113
+ */
114
+ createShader(context: FilterContext): GLShader;
115
+ /**
116
+ * Create uniform values for this filter based on user parameters.
117
+ * Called each time the filter is applied.
118
+ * @param params The parameters passed by the user (can be undefined)
119
+ * @param context The filter context containing dimensions
120
+ * @returns An object mapping uniform names to values
121
+ */
122
+ createUniforms(params: unknown, context: FilterContext): Record<string, unknown>;
123
+ }
@@ -0,0 +1,6 @@
1
+ export { TextmodeLayer } from './TextmodeLayer';
2
+ export { LayerManager as TextmodeLayerManager } from './LayerManager';
3
+ export { Layer2DCompositor as LayerCompositor } from './Layer2DCompositor';
4
+ export type { TextmodeLayerOptions, TextmodeLayerBlendMode } from './types';
5
+ export { FilterRegistry } from './filters';
6
+ export type { FilterName, BuiltInFilterName, BuiltInFilterParams, FilterContext, TextmodeFilterStrategy } from './filters';
@@ -0,0 +1,74 @@
1
+ import type { GLFramebuffer } from '../../rendering';
2
+ import type { TextmodeGrid } from '../Grid';
3
+ import type { GLRenderer } from '../../rendering/webgl/core/Renderer';
4
+ import type { TextmodeFont } from '../loadables/font';
5
+ import type { LayerFilterManager } from './filters';
6
+ /**
7
+ * Blend modes available for {@link TextmodeLayer} compositing in 2D mode.
8
+ *
9
+ * - `'normal'`: Standard alpha compositing. Opaque layer pixels fully replace the base; translucent pixels fade in.
10
+ * - `'additive'`: Layer color is added on top of the base. Great for glow/energy effects but will clip as values approach white.
11
+ * - `'multiply'`: `result = layer * base`. Darkens wherever both layers have color; any channel multiplied by 0 becomes 0.
12
+ * - `'screen'`: Inverse of multiply. `result = 1 - (1 - layer) * (1 - base)`. Preserves highlights while lightening midtones.
13
+ * - `'subtract'`: `result = base - layer`. Useful for cutting out or darkening effects.
14
+ * - `'darken'`: Takes the minimum of layer and base per channel. Only darkens; never lightens.
15
+ * - `'lighten'`: Takes the maximum of layer and base per channel. Only lightens; never darkens.
16
+ * - `'overlay'`: Combines multiply and screen. Darkens darks and lightens lights, increasing contrast.
17
+ * - `'softLight'`: Softer version of overlay. Subtle contrast enhancement.
18
+ * - `'hardLight'`: Like overlay but more intense. Uses blend color to determine multiply/screen.
19
+ * - `'colorDodge'`: Brightens the base by the blend color. Creates intense highlights.
20
+ * - `'colorBurn'`: Darkens the base by the blend color. Creates deep shadows.
21
+ * - `'difference'`: `result = |base - blend|`. Creates inverted/solarized effects.
22
+ * - `'exclusion'`: Softer version of difference. `result = base + blend - 2 * base * blend`.
23
+ */
24
+ export type TextmodeLayerBlendMode = 'normal' | 'additive' | 'multiply' | 'screen' | 'subtract' | 'darken' | 'lighten' | 'overlay' | 'softLight' | 'hardLight' | 'colorDodge' | 'colorBurn' | 'difference' | 'exclusion';
25
+ /**
26
+ * Options for configuring a new TextmodeLayer via {@link TextmodeLayerManager.add}.
27
+ */
28
+ export interface TextmodeLayerOptions {
29
+ /**
30
+ * Whether the layer is visible. Default is `true`.
31
+ */
32
+ visible?: boolean;
33
+ /**
34
+ * The opacity of the layer, between 0 (fully transparent) and 1 (fully opaque). Default is `1`.
35
+ */
36
+ opacity?: number;
37
+ /**
38
+ * The blend mode used when rendering this layer. Default is `'normal'`.
39
+ */
40
+ blendMode?: TextmodeLayerBlendMode;
41
+ /**
42
+ * The horizontal offset of the layer in pixels. Default is `0`.
43
+ */
44
+ offsetX?: number;
45
+ /**
46
+ * The vertical offset of the layer in pixels. Default is `0`.
47
+ */
48
+ offsetY?: number;
49
+ }
50
+ /**
51
+ * Dependencies required by a TextmodeLayer to function.
52
+ * @internal
53
+ */
54
+ export interface LayerDependencies {
55
+ renderer: GLRenderer;
56
+ grid: TextmodeGrid;
57
+ font: TextmodeFont;
58
+ createFramebuffer: (width: number, height: number, attachments?: number) => GLFramebuffer;
59
+ /**
60
+ * The shared filter manager for applying post-ASCII filters.
61
+ */
62
+ filterManager: LayerFilterManager;
63
+ /**
64
+ * Optional external draw framebuffer. When provided, the layer will use this
65
+ * instead of creating its own. Used for the base layer which shares the
66
+ * main textmode draw framebuffer.
67
+ */
68
+ externalDrawFramebuffer?: GLFramebuffer;
69
+ /**
70
+ * Optional external ASCII framebuffer. When provided, the layer will use this
71
+ * instead of creating its own. Used for the base layer.
72
+ */
73
+ externalAsciiFramebuffer?: GLFramebuffer;
74
+ }
@@ -68,7 +68,7 @@ export declare class LoadingScreenManager {
68
68
  * Get the current overall loading progress (0-1).
69
69
  *
70
70
  * @example
71
- * ```ts
71
+ * ```javascript
72
72
  * const t = textmode.create({
73
73
  * width: 800,
74
74
  * height: 600,
@@ -92,7 +92,7 @@ export declare class LoadingScreenManager {
92
92
  * @returns The current loading screen message.
93
93
  *
94
94
  * @example
95
- * ```ts
95
+ * ```javascript
96
96
  * const t = textmode.create({
97
97
  * width: 800,
98
98
  * height: 600,
@@ -121,7 +121,7 @@ export declare class LoadingScreenManager {
121
121
  * @returns A handle to the created loading phase.
122
122
  *
123
123
  * @example
124
- * ```ts
124
+ * ```javascript
125
125
  * const t = textmode.create({
126
126
  * width: 800,
127
127
  * height: 600,
@@ -162,7 +162,7 @@ export declare class LoadingScreenManager {
162
162
  * @param error The error message or `Error` object.
163
163
  *
164
164
  * @example
165
- * ```ts
165
+ * ```javascript
166
166
  * const t = textmode.create({
167
167
  * width: 800,
168
168
  * height: 600,
@@ -1,7 +1,12 @@
1
1
  import type { TextmodeCanvas } from '../Canvas';
2
2
  import type { TextmodeGrid } from '../Grid';
3
3
  /**
4
- * Mouse coordinates in grid space
4
+ * Mouse coordinates in grid space.
5
+ *
6
+ * Unlike the main drawing logic, where `(0,0,0)` is the center cell,
7
+ * the mouse coordinates use the top-left cell as `(0,0)`. This means
8
+ * you'll need to adjust accordingly when using these coordinates
9
+ * for drawing or other grid operations.
5
10
  */
6
11
  export interface MousePosition {
7
12
  /** Grid X coordinate (column), -1 if mouse is outside grid */
@@ -26,6 +26,11 @@ export interface TouchPosition {
26
26
  }
27
27
  /**
28
28
  * Touch event data.
29
+ *
30
+ * Unlike the main drawing logic, where `(0,0,0)` is the center cell,
31
+ * the mouse coordinates use the top-left cell as `(0,0)`. This means
32
+ * you'll need to adjust accordingly when using these coordinates
33
+ * for drawing or other grid operations.
29
34
  */
30
35
  export interface TouchEventData {
31
36
  /** The touch point that triggered this event */
@@ -70,7 +70,6 @@ export interface IKeyboardMixin {
70
70
  *
71
71
  * // Show the last pressed key at the center of the grid
72
72
  * t.push();
73
- * t.translate(t.grid.cols / 2, t.grid.rows / 2);
74
73
  * t.char(lastKey.length ? lastKey[0] : '?');
75
74
  * t.point();
76
75
  * t.pop();
@@ -12,10 +12,13 @@ export interface IRenderingMixin {
12
12
  * @param shader The custom shader to use
13
13
  *
14
14
  * @example
15
- * ```javascript
16
- * const t = textmode.create({ width: 800, height: 600 });
17
- *
18
- * const glitchShader = t.createFilterShader(`#version 300 es
15
+ * ```javascript
16
+ * const t = textmode.create({ width: 800, height: 600 });
17
+ *
18
+ * let glitchShader;
19
+ *
20
+ * t.setup(async() => {
21
+ * glitchShader = await t.createFilterShader(`#version 300 es
19
22
  * precision highp float;
20
23
  * in vec2 v_uv;
21
24
  * uniform float u_intensity;
@@ -33,11 +36,11 @@ export interface IRenderingMixin {
33
36
  * }
34
37
  * `);
35
38
  *
36
- * t.draw(() => {
37
- * t.shader(glitchShader);
38
- * t.setUniform('u_intensity', Math.sin(t.frameCount * 0.1) * 0.02);
39
- * t.rect(t.grid.cols, t.grid.rows);
40
- * });
39
+ * t.draw(() => {
40
+ * t.shader(glitchShader);
41
+ * t.setUniform('u_intensity', Math.sin(t.frameCount * 0.1) * 0.02);
42
+ * t.rect(t.grid.cols, t.grid.rows);
43
+ * });
41
44
  * ```
42
45
  */
43
46
  shader(shader: GLShader): void;
@@ -196,7 +199,10 @@ export interface IRenderingMixin {
196
199
  * ```javascript
197
200
  * const t = textmode.create({ width: 800, height: 600 });
198
201
  *
199
- * const pulseShader = t.createFilterShader(`#version 300 es
202
+ * let pulseShader;
203
+ *
204
+ * t.setup(async () => {
205
+ * pulseShader = await t.createFilterShader(`#version 300 es
200
206
  * precision highp float;
201
207
  * in vec2 v_uv;
202
208
  * uniform float u_time;
@@ -212,11 +218,12 @@ export interface IRenderingMixin {
212
218
  * o_secondaryColor = vec4(color * 0.3, 1.0);
213
219
  * }
214
220
  * `);
221
+ * });
215
222
  *
216
223
  * t.draw(() => {
217
- * t.shader(pulseShader);
218
- * t.setUniform('u_time', t.frameCount * 0.005);
219
- * t.rect(t.grid.cols, t.grid.rows);
224
+ * t.shader(pulseShader);
225
+ * t.setUniform('u_time', t.frameCount * 0.005);
226
+ * t.rect(t.grid.cols, t.grid.rows);
220
227
  * });
221
228
  * ```
222
229
  */
@@ -229,7 +236,10 @@ export interface IRenderingMixin {
229
236
  * ```javascript
230
237
  * const t = textmode.create({ width: 800, height: 600 });
231
238
  *
232
- * const rippleShader = t.createFilterShader(`#version 300 es
239
+ * let rippleShader;
240
+ *
241
+ * t.setup(async() => {
242
+ * rippleShader = await t.createFilterShader(`#version 300 es
233
243
  * precision highp float;
234
244
  * in vec2 v_uv;
235
245
  * uniform float u_time;
@@ -247,14 +257,15 @@ export interface IRenderingMixin {
247
257
  * o_secondaryColor = vec4(color * 0.4, 1.0);
248
258
  * }
249
259
  * `);
260
+ * });
250
261
  *
251
262
  * t.draw(() => {
252
- * t.shader(rippleShader);
253
- * t.setUniforms({
254
- * u_time: t.frameCount * 0.0005,
255
- * u_center: [0.5, 0.5]
256
- * });
257
- * t.rect(t.grid.cols, t.grid.rows);
263
+ * t.shader(rippleShader);
264
+ * t.setUniforms({
265
+ * u_time: t.frameCount * 0.0005,
266
+ * u_center: [0.5, 0.5]
267
+ * });
268
+ * t.rect(t.grid.cols, t.grid.rows);
258
269
  * });
259
270
  * ```
260
271
  */
@@ -310,6 +321,52 @@ export interface IRenderingMixin {
310
321
  * ```
311
322
  */
312
323
  createFilterShader(fragmentSource: string): Promise<GLShader>;
324
+ /**
325
+ * Create a custom shader from vertex and fragment shader source code or file paths.
326
+ * Both the vertex and fragment shaders can be provided as inline GLSL source code
327
+ * or as file paths (e.g., './vertex.vert', './fragment.frag').
328
+ * @param vertexSource The vertex shader source code or a file path (e.g., './shader.vert')
329
+ * @param fragmentSource The fragment shader source code or a file path (e.g., './shader.frag')
330
+ * @returns A Promise that resolves to a compiled shader ready for use with {@link shader}
331
+ *
332
+ * @example
333
+ * ```javascript
334
+ * const t = textmode.create({
335
+ * width: 800,
336
+ * height: 600,
337
+ * });
338
+ *
339
+ * let customShader;
340
+ *
341
+ * t.setup(async () => {
342
+ * // Load shaders from files
343
+ * customShader = await t.createShader('./vertex.vert', './fragment.frag');
344
+ *
345
+ * // Or create from inline source
346
+ * // customShader = await t.createShader(
347
+ * // `#version 300 es
348
+ * // in vec2 a_position;
349
+ * // void main() {
350
+ * // gl_Position = vec4(a_position, 0.0, 1.0);
351
+ * // }`,
352
+ * // `#version 300 es
353
+ * // precision highp float;
354
+ * // out vec4 fragColor;
355
+ * // void main() {
356
+ * // fragColor = vec4(1.0, 0.0, 0.0, 1.0);
357
+ * // }`
358
+ * // );
359
+ * });
360
+ *
361
+ * t.draw(() => {
362
+ * if (customShader) {
363
+ * t.shader(customShader);
364
+ * t.rect(t.grid.cols, t.grid.rows);
365
+ * }
366
+ * });
367
+ * ```
368
+ */
369
+ createShader(vertexSource: string, fragmentSource: string): Promise<GLShader>;
313
370
  /**
314
371
  * Sets the rotation angles for subsequent shape rendering operations.
315
372
  *
@@ -837,18 +894,12 @@ export interface IRenderingMixin {
837
894
  * height: 600,
838
895
  * })
839
896
  *
840
- * let semicolon;
841
- *
842
- * t.setup(() => {
843
- * semicolon = t.color(';');
844
- * });
845
- *
846
897
  * t.draw(() => {
847
898
  * t.background(0);
848
899
  * t.char('A');
849
900
  * t.rect(10, 10);
850
901
  *
851
- * t.char(semicolon);
902
+ * t.char(";");
852
903
  * t.translate(15, 0);
853
904
  * t.rect(10, 10);
854
905
  * });
@@ -922,6 +973,7 @@ export interface IRenderingMixin {
922
973
  * t.draw(() => {
923
974
  * t.background(0);
924
975
  * t.flipX(true);
976
+ * t.char('A');
925
977
  * t.rect(5, 5);
926
978
  * });
927
979
  * ```
@@ -941,6 +993,7 @@ export interface IRenderingMixin {
941
993
  * t.draw(() => {
942
994
  * t.background(0);
943
995
  * t.flipY(true);
996
+ * t.char('A');
944
997
  * t.rect(5, 5);
945
998
  * });
946
999
  * ```
@@ -177,7 +177,7 @@ export interface ITouchMixin {
177
177
  * ```javascript
178
178
  * t.draw(() => {
179
179
  * for (const touch of t.touches) {
180
- * t.point(touch.x, touch.y);
180
+ * t.point();
181
181
  * }
182
182
  * });
183
183
  * ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "textmode.js",
3
- "version": "0.6.1",
3
+ "version": "0.7.0-beta.2",
4
4
  "description": "textmode.js is a lightweight creative coding library for creating real-time ASCII art on the web.",
5
5
  "type": "module",
6
6
  "types": "./dist/types/index.d.ts",