asciify-engine 4.25.0 → 4.26.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 +10 -0
- package/dist/field.cjs +1 -1
- package/dist/field.d.cts +132 -275
- package/dist/field.d.ts +132 -275
- package/dist/field.js +1 -1
- package/dist/glyph-field-C2fQIlnq.d.cts +292 -0
- package/dist/glyph-field-DRC-7uVf.d.ts +292 -0
- package/dist/kit/{chunk-KS3TL3S2.js → chunk-2FH6GGV6.js} +1 -1
- package/dist/kit/chunk-2GAV4OAK.js +1 -0
- package/dist/kit/{chunk-3F6FTBMN.js → chunk-2LBQ7QJT.js} +1 -1
- package/dist/kit/chunk-3LJ5C3U3.js +1 -0
- package/dist/kit/{chunk-SQGJ263V.js → chunk-3MHEDZLZ.js} +1 -1
- package/dist/kit/{chunk-KTYCGGRO.js → chunk-3OD5BMF5.js} +1 -1
- package/dist/kit/{chunk-OVCIHOOS.js → chunk-3VEMKDBW.js} +1 -1
- package/dist/kit/{chunk-H4DBJULW.js → chunk-42KMN727.js} +1 -1
- package/dist/kit/{chunk-PND73YEV.js → chunk-6G5MPND3.js} +1 -1
- package/dist/kit/{chunk-CA5J25SV.js → chunk-6WU247CB.js} +1 -1
- package/dist/kit/{chunk-6L2GAV5C.js → chunk-776H2BJ7.js} +1 -1
- package/dist/kit/{chunk-4SHQA3CO.js → chunk-7DXSG526.js} +1 -1
- package/dist/kit/{chunk-L3P7ZDSK.js → chunk-7M2FFUZ3.js} +1 -1
- package/dist/kit/{chunk-XYLLL2PQ.js → chunk-7NVKRDKE.js} +1 -1
- package/dist/kit/chunk-AIGAJMNN.js +1 -0
- package/dist/kit/{chunk-XXPDXJMQ.js → chunk-B3TY6UXJ.js} +1 -1
- package/dist/kit/chunk-BFBG3VHI.js +1 -0
- package/dist/kit/{chunk-6XNIZ52V.js → chunk-CG6SGQMY.js} +1 -1
- package/dist/kit/{chunk-KBXVIAWS.js → chunk-CGSWUO67.js} +1 -1
- package/dist/kit/{chunk-YDP6566E.js → chunk-DAJSBX6H.js} +1 -1
- package/dist/kit/{chunk-PFNFW4ZI.js → chunk-DCLJHSWM.js} +1 -1
- package/dist/kit/{chunk-QTVMD62E.js → chunk-DMRPBFLW.js} +1 -1
- package/dist/kit/{chunk-CR7QS3B3.js → chunk-E3FPFANJ.js} +1 -1
- package/dist/kit/{chunk-LUOIH2EM.js → chunk-EKYEA7Z4.js} +1 -1
- package/dist/kit/{chunk-QKZYCROD.js → chunk-F6F2C3RH.js} +1 -1
- package/dist/kit/chunk-FEAFQY7O.js +1 -0
- package/dist/kit/{chunk-A6EDU3UT.js → chunk-FHN3PPSI.js} +1 -1
- package/dist/kit/{chunk-FIWIUXNV.js → chunk-FPCCX4FM.js} +1 -1
- package/dist/kit/{chunk-EHYFSX5K.js → chunk-FWODCYYJ.js} +1 -1
- package/dist/kit/{chunk-PZAQLUAS.js → chunk-GBNV3GXG.js} +1 -1
- package/dist/kit/chunk-GF72XM4C.js +1 -0
- package/dist/kit/{chunk-MRDEO5HZ.js → chunk-GKR3ROYR.js} +1 -1
- package/dist/kit/{chunk-D4KCZSIP.js → chunk-H5KHXZWR.js} +1 -1
- package/dist/kit/{chunk-EJ3W4FN6.js → chunk-I5R2347P.js} +1 -1
- package/dist/kit/{chunk-ZYYLWSPY.js → chunk-IBNDCKEX.js} +1 -1
- package/dist/kit/{chunk-A73S2BCE.js → chunk-ILXVBO7M.js} +1 -1
- package/dist/kit/{chunk-QZHZWO7N.js → chunk-JELJEEJO.js} +1 -1
- package/dist/kit/{chunk-XDNCXCR7.js → chunk-K6JX64AS.js} +1 -1
- package/dist/kit/{chunk-2D2SG6FW.js → chunk-KCEYRIU4.js} +1 -1
- package/dist/kit/{chunk-SE6MQQLB.js → chunk-KDROJANO.js} +1 -1
- package/dist/kit/{chunk-ODMQERGZ.js → chunk-KEE55QSD.js} +1 -1
- package/dist/kit/{chunk-YBBYCUFP.js → chunk-KFXMLIVF.js} +1 -1
- package/dist/kit/{chunk-P6S6HSN3.js → chunk-KLRF3S7O.js} +1 -1
- package/dist/kit/{chunk-ANLBW5RI.js → chunk-KXI3T72F.js} +1 -1
- package/dist/kit/{chunk-PDNKLLBI.js → chunk-KY5KCIZE.js} +1 -1
- package/dist/kit/{chunk-PCGRRX5P.js → chunk-LE53AVKR.js} +1 -1
- package/dist/kit/{chunk-MIA6TUQC.js → chunk-LVCGC3SO.js} +1 -1
- package/dist/kit/chunk-M5VVLYI2.js +1 -0
- package/dist/kit/{chunk-KJJJORN6.js → chunk-MJJSZS23.js} +1 -1
- package/dist/kit/{chunk-EPVZIGEZ.js → chunk-MZFK765W.js} +1 -1
- package/dist/kit/{chunk-G4BQYQJ6.js → chunk-NF4WVWAC.js} +1 -1
- package/dist/kit/{chunk-JRSCTMFM.js → chunk-NW4U2YRE.js} +1 -1
- package/dist/kit/chunk-ORSI57T2.js +1 -0
- package/dist/kit/{chunk-GCIZKMJL.js → chunk-QSTDYL35.js} +1 -1
- package/dist/kit/{chunk-B6GZ645M.js → chunk-QVICL57J.js} +1 -1
- package/dist/kit/{chunk-JAM46FF6.js → chunk-RXOPJJC4.js} +1 -1
- package/dist/kit/{chunk-YL2AXWRA.js → chunk-SDSW7422.js} +1 -1
- package/dist/kit/{chunk-WT3XOWAI.js → chunk-TRDA73HN.js} +1 -1
- package/dist/kit/{chunk-VFV7HZ3G.js → chunk-UOYBFLGJ.js} +1 -1
- package/dist/kit/{chunk-EWPT56TR.js → chunk-UVLKCAKH.js} +1 -1
- package/dist/kit/{chunk-EHXSXMBE.js → chunk-VBWG7HQA.js} +1 -1
- package/dist/kit/{chunk-2TQW2PIG.js → chunk-VIUEAEBX.js} +1 -1
- package/dist/kit/{chunk-BBOSHBZK.js → chunk-VV2KP7ZW.js} +1 -1
- package/dist/kit/{chunk-4XT5IKM3.js → chunk-VX2AD3M7.js} +1 -1
- package/dist/kit/{chunk-2KFBIOVS.js → chunk-W4U5CK6L.js} +1 -1
- package/dist/kit/{chunk-6YVZZ3DO.js → chunk-WI2AF2KE.js} +1 -1
- package/dist/kit/{chunk-HMW5NRUV.js → chunk-XUCYFWC3.js} +1 -1
- package/dist/kit/{chunk-V6GEQ5Y5.js → chunk-Z2FRNFMB.js} +1 -1
- package/dist/kit/{chunk-NPJ77LDC.js → chunk-Z3KD6CLE.js} +1 -1
- package/dist/kit/{chunk-ONVONNUH.js → chunk-ZCXNGJGB.js} +1 -1
- package/dist/kit/{chunk-IUX46UDR.js → chunk-ZWZZMU5P.js} +1 -1
- package/dist/kit.cjs +1 -1
- package/dist/kit.d.cts +1 -1
- package/dist/kit.d.ts +1 -1
- package/dist/kit.js +1 -1
- package/dist/studio-worker.js +1 -0
- package/dist/studio.cjs +1 -1
- package/dist/studio.js +1 -1
- package/package.json +2 -1
- package/skills/asciify-engine/SKILL.md +2 -2
- package/skills/asciify-engine/references/performance.md +1 -1
- package/dist/kit/chunk-2JQ4MRIP.js +0 -1
- package/dist/kit/chunk-BJRPY7UB.js +0 -1
- package/dist/kit/chunk-EK4GIXIO.js +0 -1
- package/dist/kit/chunk-PFQBEWCF.js +0 -1
- package/dist/kit/chunk-SAQJAH2I.js +0 -1
- package/dist/kit/chunk-SVXRMHUJ.js +0 -1
- package/dist/kit/chunk-UO4U32P7.js +0 -1
- package/dist/kit/chunk-XVB3B3FV.js +0 -1
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { H as HoverScope } from './hover-guide-DdFnAnfU.cjs';
|
|
2
|
+
|
|
3
|
+
/** Pure option handling for the GPU glyph field, kept free of DOM and WebGL so it can be tested in Node. */
|
|
4
|
+
|
|
5
|
+
type FieldColor = 'ink' | 'source';
|
|
6
|
+
type FieldFit = 'cover' | 'contain';
|
|
7
|
+
/**
|
|
8
|
+
* Generated motion used when there is no media source. The later ones are the kit's background scenes: caustic light, drifting contour lines, aurora ribbons, water rings, a halo,
|
|
9
|
+
* two interfering ring grids, a gradient mesh, falling streaks in three depths, and linked points.
|
|
10
|
+
*/
|
|
11
|
+
declare const FIELD_SCENES: readonly ["none", "flow", "orb", "drift", "swell", "caustics", "contour", "aurora", "ripple", "halo", "interference", "mesh", "cascade", "constellation"];
|
|
12
|
+
type FieldScene = typeof FIELD_SCENES[number];
|
|
13
|
+
/**
|
|
14
|
+
* What each cell draws. `glyph` draws characters from the atlas; `dot` draws a halftone: one circle per cell, its area
|
|
15
|
+
* following the tone. The rest are the Studio's tile and print styles, drawn per cell from the cell's tone and colour:
|
|
16
|
+
* tiles (`pixel`, `mosaic`, `lego`, `voxel`, `disco`, `hex`, `led`) fade with the tone in ink colour and stay solid in source
|
|
17
|
+
* colour; `dither` lights whole cells through a 4x4 Bayer screen; `cmyk` separates the cell's colour into offset ink dots;
|
|
18
|
+
* the print marks (`stipple`, `engraving`, `crosshatch`, `woodblock`, `risograph`, `pointillism`) carry the tone in their size.
|
|
19
|
+
* Every mark but `dot` leaves empty the cells the charset would leave blank.
|
|
20
|
+
*/
|
|
21
|
+
declare const FIELD_MARKS: readonly ["glyph", "dot", "pixel", "mosaic", "lego", "voxel", "disco", "dither", "hex", "led", "cmyk", "stipple", "engraving", "crosshatch", "woodblock", "risograph", "pointillism"];
|
|
22
|
+
type FieldMark = typeof FIELD_MARKS[number];
|
|
23
|
+
/**
|
|
24
|
+
* An area the field keeps quiet, so content above it stays readable. Coordinates are fractions of the field,
|
|
25
|
+
* from the top left; `feather` is the soft edge, as a fraction of the field's shorter side.
|
|
26
|
+
*/
|
|
27
|
+
interface FieldClearZone {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
feather: number;
|
|
33
|
+
shape: 'ellipse' | 'rect';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* How the field answers the pointer.
|
|
37
|
+
* - `fluid`: an incompressible fluid that bends the image and swirls the characters. Bold, liquid.
|
|
38
|
+
* - `lantern`: a soft light that glides after the pointer and lifts the characters it passes. Subtle, no distortion.
|
|
39
|
+
* - `trail`: the Asciify Trail. The same ink field as the studio's Trail; the wake folds each character's tone, then settles.
|
|
40
|
+
* - `loupe`: a glass loupe glides after the pointer and magnifies what it covers, easing through a soft rim. Uses `lantern` for radius, gain and follow.
|
|
41
|
+
* - `reveal`: under a soft spotlight the characters take the source's own colours. Uses `lantern` settings.
|
|
42
|
+
* - `none`: still under the pointer.
|
|
43
|
+
*/
|
|
44
|
+
type FieldHover = 'fluid' | 'lantern' | 'trail' | 'loupe' | 'reveal' | 'none';
|
|
45
|
+
declare const FIELD_HOVERS: readonly {
|
|
46
|
+
value: FieldHover;
|
|
47
|
+
label: string;
|
|
48
|
+
description: string;
|
|
49
|
+
}[];
|
|
50
|
+
interface FieldLanternOptions {
|
|
51
|
+
/** Light radius as a fraction of the field height. */
|
|
52
|
+
radius: number;
|
|
53
|
+
/** How far the light lifts characters toward denser glyphs. */
|
|
54
|
+
strength: number;
|
|
55
|
+
/** Seconds the light's tail takes to fade. 0 is no tail. */
|
|
56
|
+
trail: number;
|
|
57
|
+
/** How quickly the light catches the pointer; higher follows more tightly. */
|
|
58
|
+
follow: number;
|
|
59
|
+
}
|
|
60
|
+
declare const DEFAULT_FIELD_LANTERN: FieldLanternOptions;
|
|
61
|
+
/** Matches the studio's Trail hover settings, so a look moves between renderers unchanged. */
|
|
62
|
+
interface FieldTrailOptions {
|
|
63
|
+
/** 0–1. How far the wake folds tone. */
|
|
64
|
+
strength: number;
|
|
65
|
+
/** 0.1–1. Wake width. */
|
|
66
|
+
radius: number;
|
|
67
|
+
}
|
|
68
|
+
declare const DEFAULT_FIELD_TRAIL: FieldTrailOptions;
|
|
69
|
+
interface FieldFluidOptions {
|
|
70
|
+
/** Brush radius as a fraction of the field height. */
|
|
71
|
+
radius: number;
|
|
72
|
+
/** How strongly pointer speed pushes the fluid. */
|
|
73
|
+
force: number;
|
|
74
|
+
/** Velocity decay per second; higher settles faster. */
|
|
75
|
+
dissipation: number;
|
|
76
|
+
/** How far the fluid bends the sampled image, in field heights per unit of velocity. */
|
|
77
|
+
refraction: number;
|
|
78
|
+
/** How much a wake lifts characters toward denser glyphs and the highlight color. */
|
|
79
|
+
glow: number;
|
|
80
|
+
/** Longest side of the simulation grid in cells. */
|
|
81
|
+
resolution: number;
|
|
82
|
+
/** Vorticity: how strongly the wake curls into eddies. 0 is a straight smear. */
|
|
83
|
+
curl: number;
|
|
84
|
+
}
|
|
85
|
+
interface GlyphFieldSettings {
|
|
86
|
+
/** Ordered from empty to dense. Any graphemes, including words such as " .:-+ASCIIFY". */
|
|
87
|
+
charset: string;
|
|
88
|
+
/** Cell height in CSS pixels. */
|
|
89
|
+
cellSize: number;
|
|
90
|
+
/** Cell width divided by height. 1 is a square grid. */
|
|
91
|
+
cellAspect: number;
|
|
92
|
+
/**
|
|
93
|
+
* `pixel` (default) snaps each cell to whole device pixels. `fill` fits a whole number of cells of at least `cellSize` to the
|
|
94
|
+
* canvas and stretches them to fill it exactly, as asciify-engine's core renderer lays out a frame, so rows and columns keep their
|
|
95
|
+
* place however fractional the cell; each glyph sits centred in its cell, texel for texel.
|
|
96
|
+
*/
|
|
97
|
+
cellFit: 'pixel' | 'fill';
|
|
98
|
+
font: string;
|
|
99
|
+
fontWeight: number;
|
|
100
|
+
/** Glyph height relative to the cell. */
|
|
101
|
+
glyphScale: number;
|
|
102
|
+
/** See `FIELD_MARKS`. Only `glyph` reads the atlas; the others keep the charset for which cells stay empty. */
|
|
103
|
+
mark: FieldMark;
|
|
104
|
+
/** Largest dot as a fraction of the cell, for `mark: 'dot'`. */
|
|
105
|
+
dotScale: number;
|
|
106
|
+
color: FieldColor;
|
|
107
|
+
ink: string;
|
|
108
|
+
/** Color the wake lifts characters toward. 'auto' brightens each character's own color, so the wake never turns white. */
|
|
109
|
+
highlight: string;
|
|
110
|
+
/** Any CSS hex color, or "transparent". */
|
|
111
|
+
background: string;
|
|
112
|
+
brightness: number;
|
|
113
|
+
contrast: number;
|
|
114
|
+
gamma: number;
|
|
115
|
+
invert: boolean;
|
|
116
|
+
/** Stretch the source's tonal range so the whole charset is used. */
|
|
117
|
+
levels: 'auto' | 'none';
|
|
118
|
+
/**
|
|
119
|
+
* Shades between two characters, drawn by mixing them across neighbouring cells in a fixed 4x4 pattern, as print
|
|
120
|
+
* halftones do. 'auto' turns it on for photo and video sources drawn in nine or fewer characters, whose steps are coarse;
|
|
121
|
+
* drawn interface shapes stay crisp, and ramps of ten and more are left as they are. 'coarse' does the same for any
|
|
122
|
+
* source, for a canvas that paints photos. The pattern is locked to the grid, so it never shimmers.
|
|
123
|
+
*/
|
|
124
|
+
blend: 'auto' | 'coarse' | 'on' | 'off';
|
|
125
|
+
/** Glyph color saturation in source color mode. */
|
|
126
|
+
saturation: number;
|
|
127
|
+
/** How far darker tones dim the ink, so same-density glyphs such as words still carry the image. 0 is one flat ink. */
|
|
128
|
+
shade: number;
|
|
129
|
+
/** Opacity of a soft, blurred copy of the source behind the characters. 0 draws characters only. */
|
|
130
|
+
backdrop: number;
|
|
131
|
+
fit: FieldFit;
|
|
132
|
+
/** Crop focus for cover, 0–1 on each axis. */
|
|
133
|
+
focus: {
|
|
134
|
+
x: number;
|
|
135
|
+
y: number;
|
|
136
|
+
};
|
|
137
|
+
vignette: number;
|
|
138
|
+
/** Slow ambient sampling drift that keeps a still image alive. 0 is still. */
|
|
139
|
+
drift: number;
|
|
140
|
+
scene: FieldScene;
|
|
141
|
+
/** Size of generated scenes' features; higher is finer. */
|
|
142
|
+
scale: number;
|
|
143
|
+
clear: FieldClearZone | false;
|
|
144
|
+
/** Speed of generated scenes and drift. */
|
|
145
|
+
speed: number;
|
|
146
|
+
/** Which pointer response runs. */
|
|
147
|
+
hover: FieldHover;
|
|
148
|
+
/** Settings for the fluid hover. `false` turns it off. */
|
|
149
|
+
fluid: FieldFluidOptions | false;
|
|
150
|
+
/** Settings for the lantern hover. */
|
|
151
|
+
lantern: FieldLanternOptions;
|
|
152
|
+
/** Settings for the trail hover. */
|
|
153
|
+
trail: FieldTrailOptions;
|
|
154
|
+
/**
|
|
155
|
+
* Where the pointer response acts: `all` (default) everywhere, `subject` only on the source's subject, `outside` only on
|
|
156
|
+
* the ground around it. The subject is the source's alpha, or for an opaque source on a plain, even ground, its distance
|
|
157
|
+
* from that ground; feathered at the edge. A busy photo and generated scenes have no subject, so every scope acts as `all`.
|
|
158
|
+
*/
|
|
159
|
+
scope: HoverScope;
|
|
160
|
+
maxPixelRatio: number;
|
|
161
|
+
/** Stop ambient motion and soften the wake when the visitor prefers reduced motion. */
|
|
162
|
+
respectReducedMotion: boolean;
|
|
163
|
+
}
|
|
164
|
+
declare const DEFAULT_FIELD_FLUID: FieldFluidOptions;
|
|
165
|
+
declare const DEFAULT_FIELD_SETTINGS: GlyphFieldSettings;
|
|
166
|
+
type GlyphFieldInput = Partial<Omit<GlyphFieldSettings, 'fluid' | 'focus' | 'lantern' | 'trail' | 'clear'>> & {
|
|
167
|
+
clear?: Partial<FieldClearZone> | false;
|
|
168
|
+
trail?: Partial<FieldTrailOptions>;
|
|
169
|
+
fluid?: Partial<FieldFluidOptions> | boolean;
|
|
170
|
+
lantern?: Partial<FieldLanternOptions>;
|
|
171
|
+
focus?: Partial<GlyphFieldSettings['focus']>;
|
|
172
|
+
};
|
|
173
|
+
/** Splits a charset into graphemes so emoji, block elements and accented letters each take one cell. */
|
|
174
|
+
declare function fieldGlyphs(charset: string): string[];
|
|
175
|
+
/** Mount options may carry an element-based clear zone; normalizing ignores it, since only a mounted field can measure it. */
|
|
176
|
+
type ClearElementLike = {
|
|
177
|
+
element: unknown;
|
|
178
|
+
} | {
|
|
179
|
+
nodeType: number;
|
|
180
|
+
};
|
|
181
|
+
declare function normalizeFieldSettings(input?: Omit<GlyphFieldInput, 'clear'> & {
|
|
182
|
+
clear?: GlyphFieldInput['clear'] | ClearElementLike;
|
|
183
|
+
}, base?: GlyphFieldSettings): GlyphFieldSettings;
|
|
184
|
+
/** Maps field UV (0–1, top left) to source UV as `source = uv * scale + offset`. */
|
|
185
|
+
declare function fieldSourceRect(fieldWidth: number, fieldHeight: number, sourceWidth: number, sourceHeight: number, fit: FieldFit, focus?: {
|
|
186
|
+
x: number;
|
|
187
|
+
y: number;
|
|
188
|
+
}): {
|
|
189
|
+
scale: [number, number];
|
|
190
|
+
offset: [number, number];
|
|
191
|
+
};
|
|
192
|
+
/** Device-pixel cell and atlas layout. Cells are whole pixels so glyphs are sampled texel for texel. */
|
|
193
|
+
declare function fieldCellLayout(cellSize: number, cellAspect: number, pixelRatio: number, glyphCount: number): {
|
|
194
|
+
width: number;
|
|
195
|
+
height: number;
|
|
196
|
+
columns: number;
|
|
197
|
+
rows: number;
|
|
198
|
+
atlasWidth: number;
|
|
199
|
+
atlasHeight: number;
|
|
200
|
+
};
|
|
201
|
+
/** 2nd and 98th luminance percentiles of RGBA pixels, used to stretch a source across the charset. */
|
|
202
|
+
declare function fieldLevels(pixels: Uint8ClampedArray | Uint8Array): [number, number];
|
|
203
|
+
/** The hover that actually runs: fluid needs its settings, and anything is off without a pointer response. */
|
|
204
|
+
declare const activeFieldHover: (settings: GlyphFieldSettings) => FieldHover;
|
|
205
|
+
/** The clear zone around an element, in field fractions; `padding` is CSS pixels added on every side. */
|
|
206
|
+
declare function fieldClearFromRects(field: {
|
|
207
|
+
left: number;
|
|
208
|
+
top: number;
|
|
209
|
+
width: number;
|
|
210
|
+
height: number;
|
|
211
|
+
}, element: {
|
|
212
|
+
left: number;
|
|
213
|
+
top: number;
|
|
214
|
+
width: number;
|
|
215
|
+
height: number;
|
|
216
|
+
}, padding?: number, feather?: number, shape?: FieldClearZone['shape']): FieldClearZone | false;
|
|
217
|
+
|
|
218
|
+
type FieldSource = string | HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | ImageBitmap;
|
|
219
|
+
interface GlyphFieldOptions extends Omit<GlyphFieldInput, 'clear'> {
|
|
220
|
+
/**
|
|
221
|
+
* Keep the field quiet around content: an element (followed through layout changes), an element with
|
|
222
|
+
* `padding` (CSS px), `feather` (fraction of the shorter side) and `shape`, a zone in field fractions, or `false`.
|
|
223
|
+
*/
|
|
224
|
+
clear?: GlyphFieldInput['clear'] | HTMLElement | FieldClearElement;
|
|
225
|
+
/** Image or video URL, or a decoded element. Without one, `scene` draws generated motion. */
|
|
226
|
+
source?: FieldSource | null;
|
|
227
|
+
/** Where pointer movement is read. Defaults to the window, so overlaid text never blocks the wake. */
|
|
228
|
+
interactionTarget?: HTMLElement | Window | null;
|
|
229
|
+
/** Accessible name for the canvas. */
|
|
230
|
+
label?: string;
|
|
231
|
+
onError?: (error: Error) => void;
|
|
232
|
+
}
|
|
233
|
+
interface FieldClearElement {
|
|
234
|
+
element: HTMLElement;
|
|
235
|
+
padding?: number;
|
|
236
|
+
feather?: number;
|
|
237
|
+
shape?: 'ellipse' | 'rect';
|
|
238
|
+
}
|
|
239
|
+
interface GlyphField {
|
|
240
|
+
readonly canvas: HTMLCanvasElement;
|
|
241
|
+
/** Resolves once the first frame with the current source has been drawn. */
|
|
242
|
+
readonly ready: Promise<void>;
|
|
243
|
+
/** Merges settings; only the parts that changed are rebuilt. */
|
|
244
|
+
update(options: GlyphFieldOptions): void;
|
|
245
|
+
setSource(source: FieldSource | null): Promise<void>;
|
|
246
|
+
/** Moves the wake programmatically, in CSS pixels relative to the canvas. `null` lifts the pointer. */
|
|
247
|
+
pointer(x: number | null, y?: number): void;
|
|
248
|
+
/**
|
|
249
|
+
* Draws the current frame now, so the canvas can be read with drawImage in the same task. Before `ready`, while the shaders
|
|
250
|
+
* are still compiling in parallel, it leaves the first frame to the field's own loop rather than block on the compiler.
|
|
251
|
+
*/
|
|
252
|
+
redraw(): void;
|
|
253
|
+
/**
|
|
254
|
+
* Sends a ring through the field, for interface events such as a save or a message.
|
|
255
|
+
* `x` and `y` are CSS pixels relative to the canvas, or pass an element to start from its centre. Ignored under reduced motion.
|
|
256
|
+
*/
|
|
257
|
+
pulse(options?: {
|
|
258
|
+
x?: number;
|
|
259
|
+
y?: number;
|
|
260
|
+
element?: Element;
|
|
261
|
+
strength?: number;
|
|
262
|
+
}): void;
|
|
263
|
+
play(): void;
|
|
264
|
+
pause(): void;
|
|
265
|
+
resize(): void;
|
|
266
|
+
destroy(): void;
|
|
267
|
+
readonly settings: GlyphFieldSettings;
|
|
268
|
+
}
|
|
269
|
+
/** 'gpu', 'software' (WebGL2 emulated on the CPU, such as SwiftShader or llvmpipe) or 'none'. Probed once per page. */
|
|
270
|
+
declare function glyphFieldTier(): 'gpu' | 'software' | 'none';
|
|
271
|
+
/**
|
|
272
|
+
* Builds the glyph atlas a field with these options would build on `canvas` (laid out, in this document), in the page's shared
|
|
273
|
+
* context, and keeps it as a spare, so mounting the field later takes it as it is. Returns false where nothing was built
|
|
274
|
+
* (no shared context, a canvas without a size, an atlas the page already has).
|
|
275
|
+
*/
|
|
276
|
+
declare function prewarmGlyphAtlas(canvas: HTMLCanvasElement, options?: GlyphFieldOptions): boolean;
|
|
277
|
+
/**
|
|
278
|
+
* True when WebGL2 runs on a GPU. Software WebGL draws every pixel on the CPU and would starve the rest of the page,
|
|
279
|
+
* so it reports false unless `allowSoftware` is set. Mounting can still fail on a blocked GPU, so handle errors.
|
|
280
|
+
*/
|
|
281
|
+
declare function isGlyphFieldSupported(options?: {
|
|
282
|
+
allowSoftware?: boolean;
|
|
283
|
+
}): boolean;
|
|
284
|
+
/**
|
|
285
|
+
* A GPU character field: every cell samples the source once and draws its glyph texel for texel,
|
|
286
|
+
* while a small advected fluid bends the sampling and lifts characters under the cursor.
|
|
287
|
+
* Idle fields stop drawing until the pointer, a video frame or generated motion needs a new frame.
|
|
288
|
+
* Each frame shades one fragment per cell, then draws the glyphs per pixel, so the cost of the scene does not grow with pixel density.
|
|
289
|
+
*/
|
|
290
|
+
declare function mountGlyphField(canvas: HTMLCanvasElement, options?: GlyphFieldOptions): GlyphField;
|
|
291
|
+
|
|
292
|
+
export { isGlyphFieldSupported as A, mountGlyphField as B, normalizeFieldSettings as C, DEFAULT_FIELD_FLUID as D, prewarmGlyphAtlas as E, FIELD_HOVERS as F, type GlyphField as G, DEFAULT_FIELD_LANTERN as a, DEFAULT_FIELD_SETTINGS as b, DEFAULT_FIELD_TRAIL as c, FIELD_MARKS as d, FIELD_SCENES as e, type FieldClearElement as f, type FieldClearZone as g, type FieldColor as h, type FieldFit as i, type FieldFluidOptions as j, type FieldHover as k, type FieldLanternOptions as l, type FieldMark as m, type FieldScene as n, type FieldSource as o, type FieldTrailOptions as p, type GlyphFieldInput as q, type GlyphFieldOptions as r, type GlyphFieldSettings as s, activeFieldHover as t, fieldCellLayout as u, fieldClearFromRects as v, fieldGlyphs as w, fieldLevels as x, fieldSourceRect as y, glyphFieldTier as z };
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { H as HoverScope } from './hover-guide-DdFnAnfU.js';
|
|
2
|
+
|
|
3
|
+
/** Pure option handling for the GPU glyph field, kept free of DOM and WebGL so it can be tested in Node. */
|
|
4
|
+
|
|
5
|
+
type FieldColor = 'ink' | 'source';
|
|
6
|
+
type FieldFit = 'cover' | 'contain';
|
|
7
|
+
/**
|
|
8
|
+
* Generated motion used when there is no media source. The later ones are the kit's background scenes: caustic light, drifting contour lines, aurora ribbons, water rings, a halo,
|
|
9
|
+
* two interfering ring grids, a gradient mesh, falling streaks in three depths, and linked points.
|
|
10
|
+
*/
|
|
11
|
+
declare const FIELD_SCENES: readonly ["none", "flow", "orb", "drift", "swell", "caustics", "contour", "aurora", "ripple", "halo", "interference", "mesh", "cascade", "constellation"];
|
|
12
|
+
type FieldScene = typeof FIELD_SCENES[number];
|
|
13
|
+
/**
|
|
14
|
+
* What each cell draws. `glyph` draws characters from the atlas; `dot` draws a halftone: one circle per cell, its area
|
|
15
|
+
* following the tone. The rest are the Studio's tile and print styles, drawn per cell from the cell's tone and colour:
|
|
16
|
+
* tiles (`pixel`, `mosaic`, `lego`, `voxel`, `disco`, `hex`, `led`) fade with the tone in ink colour and stay solid in source
|
|
17
|
+
* colour; `dither` lights whole cells through a 4x4 Bayer screen; `cmyk` separates the cell's colour into offset ink dots;
|
|
18
|
+
* the print marks (`stipple`, `engraving`, `crosshatch`, `woodblock`, `risograph`, `pointillism`) carry the tone in their size.
|
|
19
|
+
* Every mark but `dot` leaves empty the cells the charset would leave blank.
|
|
20
|
+
*/
|
|
21
|
+
declare const FIELD_MARKS: readonly ["glyph", "dot", "pixel", "mosaic", "lego", "voxel", "disco", "dither", "hex", "led", "cmyk", "stipple", "engraving", "crosshatch", "woodblock", "risograph", "pointillism"];
|
|
22
|
+
type FieldMark = typeof FIELD_MARKS[number];
|
|
23
|
+
/**
|
|
24
|
+
* An area the field keeps quiet, so content above it stays readable. Coordinates are fractions of the field,
|
|
25
|
+
* from the top left; `feather` is the soft edge, as a fraction of the field's shorter side.
|
|
26
|
+
*/
|
|
27
|
+
interface FieldClearZone {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
feather: number;
|
|
33
|
+
shape: 'ellipse' | 'rect';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* How the field answers the pointer.
|
|
37
|
+
* - `fluid`: an incompressible fluid that bends the image and swirls the characters. Bold, liquid.
|
|
38
|
+
* - `lantern`: a soft light that glides after the pointer and lifts the characters it passes. Subtle, no distortion.
|
|
39
|
+
* - `trail`: the Asciify Trail. The same ink field as the studio's Trail; the wake folds each character's tone, then settles.
|
|
40
|
+
* - `loupe`: a glass loupe glides after the pointer and magnifies what it covers, easing through a soft rim. Uses `lantern` for radius, gain and follow.
|
|
41
|
+
* - `reveal`: under a soft spotlight the characters take the source's own colours. Uses `lantern` settings.
|
|
42
|
+
* - `none`: still under the pointer.
|
|
43
|
+
*/
|
|
44
|
+
type FieldHover = 'fluid' | 'lantern' | 'trail' | 'loupe' | 'reveal' | 'none';
|
|
45
|
+
declare const FIELD_HOVERS: readonly {
|
|
46
|
+
value: FieldHover;
|
|
47
|
+
label: string;
|
|
48
|
+
description: string;
|
|
49
|
+
}[];
|
|
50
|
+
interface FieldLanternOptions {
|
|
51
|
+
/** Light radius as a fraction of the field height. */
|
|
52
|
+
radius: number;
|
|
53
|
+
/** How far the light lifts characters toward denser glyphs. */
|
|
54
|
+
strength: number;
|
|
55
|
+
/** Seconds the light's tail takes to fade. 0 is no tail. */
|
|
56
|
+
trail: number;
|
|
57
|
+
/** How quickly the light catches the pointer; higher follows more tightly. */
|
|
58
|
+
follow: number;
|
|
59
|
+
}
|
|
60
|
+
declare const DEFAULT_FIELD_LANTERN: FieldLanternOptions;
|
|
61
|
+
/** Matches the studio's Trail hover settings, so a look moves between renderers unchanged. */
|
|
62
|
+
interface FieldTrailOptions {
|
|
63
|
+
/** 0–1. How far the wake folds tone. */
|
|
64
|
+
strength: number;
|
|
65
|
+
/** 0.1–1. Wake width. */
|
|
66
|
+
radius: number;
|
|
67
|
+
}
|
|
68
|
+
declare const DEFAULT_FIELD_TRAIL: FieldTrailOptions;
|
|
69
|
+
interface FieldFluidOptions {
|
|
70
|
+
/** Brush radius as a fraction of the field height. */
|
|
71
|
+
radius: number;
|
|
72
|
+
/** How strongly pointer speed pushes the fluid. */
|
|
73
|
+
force: number;
|
|
74
|
+
/** Velocity decay per second; higher settles faster. */
|
|
75
|
+
dissipation: number;
|
|
76
|
+
/** How far the fluid bends the sampled image, in field heights per unit of velocity. */
|
|
77
|
+
refraction: number;
|
|
78
|
+
/** How much a wake lifts characters toward denser glyphs and the highlight color. */
|
|
79
|
+
glow: number;
|
|
80
|
+
/** Longest side of the simulation grid in cells. */
|
|
81
|
+
resolution: number;
|
|
82
|
+
/** Vorticity: how strongly the wake curls into eddies. 0 is a straight smear. */
|
|
83
|
+
curl: number;
|
|
84
|
+
}
|
|
85
|
+
interface GlyphFieldSettings {
|
|
86
|
+
/** Ordered from empty to dense. Any graphemes, including words such as " .:-+ASCIIFY". */
|
|
87
|
+
charset: string;
|
|
88
|
+
/** Cell height in CSS pixels. */
|
|
89
|
+
cellSize: number;
|
|
90
|
+
/** Cell width divided by height. 1 is a square grid. */
|
|
91
|
+
cellAspect: number;
|
|
92
|
+
/**
|
|
93
|
+
* `pixel` (default) snaps each cell to whole device pixels. `fill` fits a whole number of cells of at least `cellSize` to the
|
|
94
|
+
* canvas and stretches them to fill it exactly, as asciify-engine's core renderer lays out a frame, so rows and columns keep their
|
|
95
|
+
* place however fractional the cell; each glyph sits centred in its cell, texel for texel.
|
|
96
|
+
*/
|
|
97
|
+
cellFit: 'pixel' | 'fill';
|
|
98
|
+
font: string;
|
|
99
|
+
fontWeight: number;
|
|
100
|
+
/** Glyph height relative to the cell. */
|
|
101
|
+
glyphScale: number;
|
|
102
|
+
/** See `FIELD_MARKS`. Only `glyph` reads the atlas; the others keep the charset for which cells stay empty. */
|
|
103
|
+
mark: FieldMark;
|
|
104
|
+
/** Largest dot as a fraction of the cell, for `mark: 'dot'`. */
|
|
105
|
+
dotScale: number;
|
|
106
|
+
color: FieldColor;
|
|
107
|
+
ink: string;
|
|
108
|
+
/** Color the wake lifts characters toward. 'auto' brightens each character's own color, so the wake never turns white. */
|
|
109
|
+
highlight: string;
|
|
110
|
+
/** Any CSS hex color, or "transparent". */
|
|
111
|
+
background: string;
|
|
112
|
+
brightness: number;
|
|
113
|
+
contrast: number;
|
|
114
|
+
gamma: number;
|
|
115
|
+
invert: boolean;
|
|
116
|
+
/** Stretch the source's tonal range so the whole charset is used. */
|
|
117
|
+
levels: 'auto' | 'none';
|
|
118
|
+
/**
|
|
119
|
+
* Shades between two characters, drawn by mixing them across neighbouring cells in a fixed 4x4 pattern, as print
|
|
120
|
+
* halftones do. 'auto' turns it on for photo and video sources drawn in nine or fewer characters, whose steps are coarse;
|
|
121
|
+
* drawn interface shapes stay crisp, and ramps of ten and more are left as they are. 'coarse' does the same for any
|
|
122
|
+
* source, for a canvas that paints photos. The pattern is locked to the grid, so it never shimmers.
|
|
123
|
+
*/
|
|
124
|
+
blend: 'auto' | 'coarse' | 'on' | 'off';
|
|
125
|
+
/** Glyph color saturation in source color mode. */
|
|
126
|
+
saturation: number;
|
|
127
|
+
/** How far darker tones dim the ink, so same-density glyphs such as words still carry the image. 0 is one flat ink. */
|
|
128
|
+
shade: number;
|
|
129
|
+
/** Opacity of a soft, blurred copy of the source behind the characters. 0 draws characters only. */
|
|
130
|
+
backdrop: number;
|
|
131
|
+
fit: FieldFit;
|
|
132
|
+
/** Crop focus for cover, 0–1 on each axis. */
|
|
133
|
+
focus: {
|
|
134
|
+
x: number;
|
|
135
|
+
y: number;
|
|
136
|
+
};
|
|
137
|
+
vignette: number;
|
|
138
|
+
/** Slow ambient sampling drift that keeps a still image alive. 0 is still. */
|
|
139
|
+
drift: number;
|
|
140
|
+
scene: FieldScene;
|
|
141
|
+
/** Size of generated scenes' features; higher is finer. */
|
|
142
|
+
scale: number;
|
|
143
|
+
clear: FieldClearZone | false;
|
|
144
|
+
/** Speed of generated scenes and drift. */
|
|
145
|
+
speed: number;
|
|
146
|
+
/** Which pointer response runs. */
|
|
147
|
+
hover: FieldHover;
|
|
148
|
+
/** Settings for the fluid hover. `false` turns it off. */
|
|
149
|
+
fluid: FieldFluidOptions | false;
|
|
150
|
+
/** Settings for the lantern hover. */
|
|
151
|
+
lantern: FieldLanternOptions;
|
|
152
|
+
/** Settings for the trail hover. */
|
|
153
|
+
trail: FieldTrailOptions;
|
|
154
|
+
/**
|
|
155
|
+
* Where the pointer response acts: `all` (default) everywhere, `subject` only on the source's subject, `outside` only on
|
|
156
|
+
* the ground around it. The subject is the source's alpha, or for an opaque source on a plain, even ground, its distance
|
|
157
|
+
* from that ground; feathered at the edge. A busy photo and generated scenes have no subject, so every scope acts as `all`.
|
|
158
|
+
*/
|
|
159
|
+
scope: HoverScope;
|
|
160
|
+
maxPixelRatio: number;
|
|
161
|
+
/** Stop ambient motion and soften the wake when the visitor prefers reduced motion. */
|
|
162
|
+
respectReducedMotion: boolean;
|
|
163
|
+
}
|
|
164
|
+
declare const DEFAULT_FIELD_FLUID: FieldFluidOptions;
|
|
165
|
+
declare const DEFAULT_FIELD_SETTINGS: GlyphFieldSettings;
|
|
166
|
+
type GlyphFieldInput = Partial<Omit<GlyphFieldSettings, 'fluid' | 'focus' | 'lantern' | 'trail' | 'clear'>> & {
|
|
167
|
+
clear?: Partial<FieldClearZone> | false;
|
|
168
|
+
trail?: Partial<FieldTrailOptions>;
|
|
169
|
+
fluid?: Partial<FieldFluidOptions> | boolean;
|
|
170
|
+
lantern?: Partial<FieldLanternOptions>;
|
|
171
|
+
focus?: Partial<GlyphFieldSettings['focus']>;
|
|
172
|
+
};
|
|
173
|
+
/** Splits a charset into graphemes so emoji, block elements and accented letters each take one cell. */
|
|
174
|
+
declare function fieldGlyphs(charset: string): string[];
|
|
175
|
+
/** Mount options may carry an element-based clear zone; normalizing ignores it, since only a mounted field can measure it. */
|
|
176
|
+
type ClearElementLike = {
|
|
177
|
+
element: unknown;
|
|
178
|
+
} | {
|
|
179
|
+
nodeType: number;
|
|
180
|
+
};
|
|
181
|
+
declare function normalizeFieldSettings(input?: Omit<GlyphFieldInput, 'clear'> & {
|
|
182
|
+
clear?: GlyphFieldInput['clear'] | ClearElementLike;
|
|
183
|
+
}, base?: GlyphFieldSettings): GlyphFieldSettings;
|
|
184
|
+
/** Maps field UV (0–1, top left) to source UV as `source = uv * scale + offset`. */
|
|
185
|
+
declare function fieldSourceRect(fieldWidth: number, fieldHeight: number, sourceWidth: number, sourceHeight: number, fit: FieldFit, focus?: {
|
|
186
|
+
x: number;
|
|
187
|
+
y: number;
|
|
188
|
+
}): {
|
|
189
|
+
scale: [number, number];
|
|
190
|
+
offset: [number, number];
|
|
191
|
+
};
|
|
192
|
+
/** Device-pixel cell and atlas layout. Cells are whole pixels so glyphs are sampled texel for texel. */
|
|
193
|
+
declare function fieldCellLayout(cellSize: number, cellAspect: number, pixelRatio: number, glyphCount: number): {
|
|
194
|
+
width: number;
|
|
195
|
+
height: number;
|
|
196
|
+
columns: number;
|
|
197
|
+
rows: number;
|
|
198
|
+
atlasWidth: number;
|
|
199
|
+
atlasHeight: number;
|
|
200
|
+
};
|
|
201
|
+
/** 2nd and 98th luminance percentiles of RGBA pixels, used to stretch a source across the charset. */
|
|
202
|
+
declare function fieldLevels(pixels: Uint8ClampedArray | Uint8Array): [number, number];
|
|
203
|
+
/** The hover that actually runs: fluid needs its settings, and anything is off without a pointer response. */
|
|
204
|
+
declare const activeFieldHover: (settings: GlyphFieldSettings) => FieldHover;
|
|
205
|
+
/** The clear zone around an element, in field fractions; `padding` is CSS pixels added on every side. */
|
|
206
|
+
declare function fieldClearFromRects(field: {
|
|
207
|
+
left: number;
|
|
208
|
+
top: number;
|
|
209
|
+
width: number;
|
|
210
|
+
height: number;
|
|
211
|
+
}, element: {
|
|
212
|
+
left: number;
|
|
213
|
+
top: number;
|
|
214
|
+
width: number;
|
|
215
|
+
height: number;
|
|
216
|
+
}, padding?: number, feather?: number, shape?: FieldClearZone['shape']): FieldClearZone | false;
|
|
217
|
+
|
|
218
|
+
type FieldSource = string | HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | ImageBitmap;
|
|
219
|
+
interface GlyphFieldOptions extends Omit<GlyphFieldInput, 'clear'> {
|
|
220
|
+
/**
|
|
221
|
+
* Keep the field quiet around content: an element (followed through layout changes), an element with
|
|
222
|
+
* `padding` (CSS px), `feather` (fraction of the shorter side) and `shape`, a zone in field fractions, or `false`.
|
|
223
|
+
*/
|
|
224
|
+
clear?: GlyphFieldInput['clear'] | HTMLElement | FieldClearElement;
|
|
225
|
+
/** Image or video URL, or a decoded element. Without one, `scene` draws generated motion. */
|
|
226
|
+
source?: FieldSource | null;
|
|
227
|
+
/** Where pointer movement is read. Defaults to the window, so overlaid text never blocks the wake. */
|
|
228
|
+
interactionTarget?: HTMLElement | Window | null;
|
|
229
|
+
/** Accessible name for the canvas. */
|
|
230
|
+
label?: string;
|
|
231
|
+
onError?: (error: Error) => void;
|
|
232
|
+
}
|
|
233
|
+
interface FieldClearElement {
|
|
234
|
+
element: HTMLElement;
|
|
235
|
+
padding?: number;
|
|
236
|
+
feather?: number;
|
|
237
|
+
shape?: 'ellipse' | 'rect';
|
|
238
|
+
}
|
|
239
|
+
interface GlyphField {
|
|
240
|
+
readonly canvas: HTMLCanvasElement;
|
|
241
|
+
/** Resolves once the first frame with the current source has been drawn. */
|
|
242
|
+
readonly ready: Promise<void>;
|
|
243
|
+
/** Merges settings; only the parts that changed are rebuilt. */
|
|
244
|
+
update(options: GlyphFieldOptions): void;
|
|
245
|
+
setSource(source: FieldSource | null): Promise<void>;
|
|
246
|
+
/** Moves the wake programmatically, in CSS pixels relative to the canvas. `null` lifts the pointer. */
|
|
247
|
+
pointer(x: number | null, y?: number): void;
|
|
248
|
+
/**
|
|
249
|
+
* Draws the current frame now, so the canvas can be read with drawImage in the same task. Before `ready`, while the shaders
|
|
250
|
+
* are still compiling in parallel, it leaves the first frame to the field's own loop rather than block on the compiler.
|
|
251
|
+
*/
|
|
252
|
+
redraw(): void;
|
|
253
|
+
/**
|
|
254
|
+
* Sends a ring through the field, for interface events such as a save or a message.
|
|
255
|
+
* `x` and `y` are CSS pixels relative to the canvas, or pass an element to start from its centre. Ignored under reduced motion.
|
|
256
|
+
*/
|
|
257
|
+
pulse(options?: {
|
|
258
|
+
x?: number;
|
|
259
|
+
y?: number;
|
|
260
|
+
element?: Element;
|
|
261
|
+
strength?: number;
|
|
262
|
+
}): void;
|
|
263
|
+
play(): void;
|
|
264
|
+
pause(): void;
|
|
265
|
+
resize(): void;
|
|
266
|
+
destroy(): void;
|
|
267
|
+
readonly settings: GlyphFieldSettings;
|
|
268
|
+
}
|
|
269
|
+
/** 'gpu', 'software' (WebGL2 emulated on the CPU, such as SwiftShader or llvmpipe) or 'none'. Probed once per page. */
|
|
270
|
+
declare function glyphFieldTier(): 'gpu' | 'software' | 'none';
|
|
271
|
+
/**
|
|
272
|
+
* Builds the glyph atlas a field with these options would build on `canvas` (laid out, in this document), in the page's shared
|
|
273
|
+
* context, and keeps it as a spare, so mounting the field later takes it as it is. Returns false where nothing was built
|
|
274
|
+
* (no shared context, a canvas without a size, an atlas the page already has).
|
|
275
|
+
*/
|
|
276
|
+
declare function prewarmGlyphAtlas(canvas: HTMLCanvasElement, options?: GlyphFieldOptions): boolean;
|
|
277
|
+
/**
|
|
278
|
+
* True when WebGL2 runs on a GPU. Software WebGL draws every pixel on the CPU and would starve the rest of the page,
|
|
279
|
+
* so it reports false unless `allowSoftware` is set. Mounting can still fail on a blocked GPU, so handle errors.
|
|
280
|
+
*/
|
|
281
|
+
declare function isGlyphFieldSupported(options?: {
|
|
282
|
+
allowSoftware?: boolean;
|
|
283
|
+
}): boolean;
|
|
284
|
+
/**
|
|
285
|
+
* A GPU character field: every cell samples the source once and draws its glyph texel for texel,
|
|
286
|
+
* while a small advected fluid bends the sampling and lifts characters under the cursor.
|
|
287
|
+
* Idle fields stop drawing until the pointer, a video frame or generated motion needs a new frame.
|
|
288
|
+
* Each frame shades one fragment per cell, then draws the glyphs per pixel, so the cost of the scene does not grow with pixel density.
|
|
289
|
+
*/
|
|
290
|
+
declare function mountGlyphField(canvas: HTMLCanvasElement, options?: GlyphFieldOptions): GlyphField;
|
|
291
|
+
|
|
292
|
+
export { isGlyphFieldSupported as A, mountGlyphField as B, normalizeFieldSettings as C, DEFAULT_FIELD_FLUID as D, prewarmGlyphAtlas as E, FIELD_HOVERS as F, type GlyphField as G, DEFAULT_FIELD_LANTERN as a, DEFAULT_FIELD_SETTINGS as b, DEFAULT_FIELD_TRAIL as c, FIELD_MARKS as d, FIELD_SCENES as e, type FieldClearElement as f, type FieldClearZone as g, type FieldColor as h, type FieldFit as i, type FieldFluidOptions as j, type FieldHover as k, type FieldLanternOptions as l, type FieldMark as m, type FieldScene as n, type FieldSource as o, type FieldTrailOptions as p, type GlyphFieldInput as q, type GlyphFieldOptions as r, type GlyphFieldSettings as s, activeFieldHover as t, fieldCellLayout as u, fieldClearFromRects as v, fieldGlyphs as w, fieldLevels as x, fieldSourceRect as y, glyphFieldTier as z };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{styleKeeper as t,srOnly as e,mountSurface as n,house as o,handle as r,clamp as l,smoothstep as a,hash as i}from"./chunk-
|
|
1
|
+
import{styleKeeper as t,srOnly as e,mountSurface as n,house as o,handle as r,clamp as l,smoothstep as a,hash as i}from"./chunk-GF72XM4C.js";var c={value:0,duration:360,stagger:60,glyphs:"#%*+=-:",format:t=>t.toLocaleString(),face:"text"};function s(t,e){const n=(t,e)=>t[t.length-1-e]??"";for(let o=e.length-1;o>=0;o--)if(/\d/.test(n(e,o))&&n(t,o)!==n(e,o))return o;return-1}function f(f,u={}){const p=parseFloat((f.textContent??"").replace(/[^\d.-]/g,"")),h={current:{...c,...u,value:u.value??(Number.isFinite(p)?p:0)}},g=t();let d=h.current.value,m=h.current.format(d),y={from:m,to:m,t0:-1e9,up:!0,first:-1},x=null;const v=e(m,!0),k=document.createElement("span"),C=()=>{k.replaceChildren(...[...m].map(t=>Object.assign(document.createElement("span"),{textContent:t}))),v.textContent=m};x=n({host:f,look:()=>({mark:"glyph",charset:" .:-=+*#%@",font:'"Geist Mono", ui-monospace, monospace',fontWeight:500,cellSize:3,cellAspect:.66,glyphScale:1,shade:.2,contrast:1.1,...h.current.look}),ink:()=>h.current.ink??getComputedStyle(f).color,kit:h,reducedMotion:()=>h.current.reducedMotion,resolution:2,onFail:()=>{x=null,f.textContent=m,g.restore()},paint:({pen:t,now:e,still:n,box:r,scale:c})=>{const s=getComputedStyle(k),f=parseFloat(s.fontSize),u=parseFloat(s.letterSpacing)||0,p=`${s.fontStyle} ${s.fontWeight} ${s.fontSize} ${s.fontFamily}`;t.setTransform(c,0,0,c,0,0),t.font=p,t.textAlign="center",t.textBaseline="alphabetic";const g=t.measureText("0"),d=g.fontBoundingBoxAscent||.8*f,m=g.fontBoundingBoxDescent||.2*f,{from:x,to:v,t0:C,up:S,first:b}=y,F=[...k.children],A=h.current,M="glyph"===A.face,w=(t,e)=>t[t.length-1-e]??"";let B=!1;return F.forEach((c,s)=>{const h=F.length-1-s,g=r(c),y=g.x+(g.w-u)/2,k=g.y,$=g.h,E=k+($-d-m)/2+d,G=w(v,h),T=b-h,z=!n&&b>=0&&T>=0&&/\d/.test(G)?l((e-C)/(A.duration+T*A.stagger)):1,N=o(z),j=z<1&&!M?a(.72,1,z).toFixed(3):"";if(c.style.opacity!==j&&(c.style.opacity=j),z>=1)return void(M&&(t.globalAlpha=1,t.font=p,t.fillStyle="#fff",t.fillText(G,y,E)));B=!0,t.globalAlpha=M?1:1-a(.72,1,z);const P=5+2*T,W=[w(x,h),...Array.from({length:P},(t,e)=>A.glyphs[Math.floor(i(e+7*h,C)*A.glyphs.length)]??""),G];S||W.reverse();const D=(W.length-1)*$,I=S?N*D:D-N*D;t.save(),t.beginPath(),t.rect(y-g.w,k,2*g.w,$),t.clip(),W.forEach((e,n)=>{const o=E+n*$-I;if(o<k-$||o>k+2*$)return;const r=n>0&&n<W.length-1;t.font=r?`600 ${.84*f}px "Geist Mono", ui-monospace, monospace`:p,t.fillStyle=r?"#d0d0d0":"#fff",t.fillText(e,y,o)}),t.restore()}),t.globalAlpha=1,B}});const S=()=>{x&&g.set(k,"color","glyph"===h.current.face?"transparent":"inherit")};x&&(g.set(k,"display","inline-flex"),S(),k.setAttribute("aria-hidden","true"),f.replaceChildren(v,k,...(t=>[...t.children].filter(t=>"IMG"===t.tagName&&void 0!==t.dataset?.asciiPoster))(f),x.canvas),C(),document.fonts?.ready.then(()=>x?.kick()));const b=t=>{if(!Number.isFinite(t))return;const e=d,n=m;d=h.current.value=t,m=h.current.format(t),x?n!==m&&(y={from:n,to:m,up:t>=e,first:s(n,m),t0:performance.now()},C(),x.kick()):f.textContent=m};return r(()=>x,h,t=>{S(),t.value!==d?b(t.value):(m=t.format(d),C(),x?.refresh())},{set:b,get value(){return d}},()=>{x&&(f.textContent=m),g.restore()})}var u=c;export{u as ODOMETER_DEFAULTS,s as firstChanged,f as odometer};//# sourceMappingURL=chunk-2FH6GGV6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parseColor as t,MONO as e,pixelSnap as n,fbm as o,hash2 as a,band as r,ditherIndex as i,toneAlpha as c}from"./chunk-ZCHGV2CV.js";import{mountCanvas as h}from"./chunk-3OD5BMF5.js";import{hexRgb as s}from"./chunk-GF72XM4C.js";function l(h,s,l,f,M={x:.5,y:.5},m={}){const{fontSize:g=13,charAspect:u=.62,lineHeightRatio:d=1.3,chars:p=" .:-=+*#%@",baseColor:x=null,accentColor:$,accentThreshold:b=.52,mouseInfluence:S=.4,mouseFalloff:C=2.8,speed:k=1,vortex:v=!0,sparkles:y=!1,breathe:z=!0,lightMode:j=!1}=m,w=Array.from(p),A=g*u,F=g*d,B=Math.ceil(s/A),G=Math.ceil(l/F),H=M.x,R=M.y,T=$?t($):null;h.clearRect(0,0,s,l),h.font=`${g}px ${e}`;const V=n(h);h.textBaseline="top";const q=f*k,D=z?.12*(.5*Math.sin(.22*q)+.5):0;for(let t=0;t<G;t++)for(let e=0;e<B;e++){const n=e/B,s=t/G,l=.045,f=.08*q,M=.06,g=.45*((.5*Math.sin(.08*e+.05*t+.6*q)+.5+(.5*Math.sin(.03*e-.07*t+.4*q)+.5)+(.5*Math.sin(.05*e+.03*t+.8*q)+.5))/3)+.35*(.5*o(e*l+f,t*l*1.4-.7*f)+.5)+.2*(.5*Math.sin((e+.65*t)*M+1.1*q)+.5)+D,u=n-H,d=s-R,p=Math.sqrt(u*u+d*d);let $=0;if(v&&p<.35){const t=Math.atan2(d,u),e=Math.sin(4*t+2.2*q-14*p),n=Math.max(0,1-p/.35);$=e*n*n*.22}const k=g*(1-S)+(Math.max(0,1-p*C)+.5*$)*S+.15*$,z=Math.min(1,Math.max(0,k));let I=z;if(y&&z>.72){const n=Math.floor(8*q),o=a(7*e+3*n,11*t+n);o>.88&&(I=Math.min(1,z+4*(o-.88)))}const O=r(I,.32,.9);if(O<=0)continue;const W=i(O,w.length,e,t,m.ditherStrength);if(" "===w[W])continue;const X=c(O);h.fillStyle=T&&I>b?`rgba(${T.r},${T.g},${T.b},${X})`:x?x.replace("{a}",String(X)):j?`rgba(111,109,103,${X})`:`rgba(143,141,134,${X})`,h.fillText(w[W],V(e*A),V(t*F))}}var f={id:"wave",name:"Wave",description:"An interference field with a soft vortex at the cursor and a slow breath.",cellSize:13,speed:.6,cursor:!0,mount:t=>h(t,(t,e,n,o,a,r)=>{const[i,c,h]=s(r.ink);l(t,e,n,o,a,{ditherStrength:r.ditherStrength,fontSize:r.fontSize,chars:r.chars,lightMode:r.lightMode,baseColor:`rgba(${i},${c},${h},{a})`,accentColor:r.accent})})};export{l as renderWaveBackground,f as wave};//# sourceMappingURL=chunk-2GAV4OAK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mountPageField as e,pageHandle as t}from"./chunk-
|
|
1
|
+
import{mountPageField as e,pageHandle as t}from"./chunk-7DXSG526.js";import{styleKeeper as r,prefersReduced as o}from"./chunk-GF72XM4C.js";var n="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:inherit;pointer-events:none";function s(s,i={}){const l={current:{...i}},a=r(),c=[],p=s.querySelector("video"),u=l.current.src??p??null,d=()=>l.current.reducedMotion??o();let y=null,h=0;y=u?e({host:s,layer:"over",look:()=>({mark:"glyph",scene:"none",charset:" .:-=+*#%@",font:'"Geist", Arial, sans-serif',fontWeight:500,cellSize:6,cellAspect:.8,glyphScale:.8,color:"ink",shade:0,contrast:1.25,levels:"auto",fit:"cover",focus:{x:.5,y:.5},hover:"trail",trail:{strength:.9,radius:.35},...l.current.look}),ink:()=>l.current.ink??"--ascii-ink-strong",source:u,label:l.current.label??"Video, drawn in characters",kit:l,ownPoster:!0,reducedMotion:()=>l.current.reducedMotion,onFail:()=>{y=null,a.restore(),v()},onRelease:()=>{f=!1,g(),u instanceof HTMLVideoElement&&u.pause?.()},onMount:()=>{h++&&(y?.field.ready.then(()=>{f=!0,g()}),k&&u instanceof HTMLVideoElement&&u.play?.()?.catch?.(()=>{}))}}):null,y&&u instanceof HTMLVideoElement&&(s.contains(u)&&a.set(u,"opacity","0"),u.muted=!0,u.loop=!0,u.playsInline=!0);let m=null;l.current.poster&&u&&(m=Object.assign(document.createElement("img"),{alt:"",decoding:"async"}),m.className="ascii-video-poster",m.setAttribute("aria-hidden","true"),m.setAttribute("style",`${n};transition:opacity 280ms cubic-bezier(.16, 1, .3, 1)`),m.src=l.current.poster,"static"===getComputedStyle(s).position&&a.set(s,"position","relative"),s.appendChild(m),c.push(m));let f=!1;const g=()=>{m&&(m.style.opacity=y&&f&&(k||!d())?"0":"1")};let b=null;function v(){!b&&u&&(u instanceof HTMLVideoElement?b=u:(b=document.createElement("video"),b.src=u,b.setAttribute("style",n),b.setAttribute("aria-label",l.current.label??"Video"),"static"===getComputedStyle(s).position&&a.set(s,"position","relative"),m?s.insertBefore(b,m):s.appendChild(b),c.push(b)),l.current.poster&&!b.poster&&(b.poster=l.current.poster),b.muted=!0,b.loop=!0,b.playsInline=!0,m&&(m.style.opacity="0"),k&&b.play?.()?.catch?.(()=>{}))}let k=l.current.autoplay??!(y?y.still():d());y?(y.field.ready.then(()=>{f=!0,k||y?.field.pause(),g()}),k&&u instanceof HTMLVideoElement&&u.play?.()?.catch?.(()=>{}),g()):v();const M=t(()=>y,l,e=>{m&&e.poster&&m.getAttribute("src")!==e.poster&&(m.src=e.poster),b&&e.poster&&(b.poster=e.poster)},{toggle:()=>(k?M.pause():M.play(),k),get playing(){return k}},()=>{c.forEach(e=>e.remove()),a.restore()}),E=M.play,A=M.pause;return M.play=()=>{y&&!M.supported||(k=!0,y?E():b?.play?.()?.catch?.(()=>{}),g())},M.pause=()=>{y&&!M.supported||(k=!1,y?A():b?.pause?.(),g())},M}export{s as asciiVideo};//# sourceMappingURL=chunk-2LBQ7QJT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{MONO as t,GRAPHITE_LIGHT as o,GRAPHITE_DARK as e,pixelSnap as n,parseColor as r,hash2 as c,band as h,ditherIndex as i,toneAlpha as l}from"./chunk-ZCHGV2CV.js";import{mountCanvas as a}from"./chunk-3OD5BMF5.js";function s(a,s,f,M,g={}){const{fontSize:d=13,color:u,accentColor:S,speed:$=.4,layers:I=4,turbulence:P=.8,lightMode:b=!1}=g,p=.62*d,m=1.3*d,k=Math.ceil(s/p),x=Math.ceil(f/m);a.clearRect(0,0,s,f),a.font=`${d}px ${t}`,a.textBaseline="top";const z=b?o:e;let C=z.r,w=z.g,y=z.b;const j=n(a);if(u){const t=r(u);t&&(C=t.r,w=t.g,y=t.b)}let v=C,B=w,V=y;const A=S?r(S):null;A&&(v=A.r,B=A.g,V=A.b);const D=M*$,F=g.chars?Array.from(g.chars):void 0,G=["-","\\","|","/"];for(let t=0;t<x;t++){const o=t/x;for(let e=0;e<k;e++){const n=e/k;let r=0,s=0;for(let t=0;t<I;t++){const e=c(13*t,7*t+3),h=1.1+2.4*e,i=.9+2*c(29*t,11*t+1),l=e*Math.PI*6,a=(.2+.5*c(41*t,17*t))*(t%2==0?1:-1.3),f=Math.sin(n*h*Math.PI*2+D*a+l),M=Math.cos(o*i*Math.PI*2+D*a*.6+1.7*l),g=Math.sin(n*i*Math.PI*P+o*h*Math.PI*P+D*a*.4);r+=Math.atan2(M+.3*g,f),s+=.5*(f*M+1)}const f=r/I,M=h(s/I,.54,.74);if(M<=0)continue;const d=(f%Math.PI+Math.PI)%Math.PI,u=Math.round(d/(Math.PI/4))%G.length;if(!F&&g.ditherStrength&&0===i(M,2,e,t,g.ditherStrength))continue;const S=F?F[i(M,F.length,e,t,g.ditherStrength)]:G[u];if(" "===S)continue;const $=!!A&&M>.85,b=l(M);a.fillStyle=$?`rgba(${v},${B},${V},${b})`:`rgba(${C},${w},${y},${b})`,a.fillText(S,j(e*p),j(t*m))}}}var f={id:"silk",name:"Silk",description:"Long ribbons that follow a slow flow field.",cellSize:13,speed:1,cursor:!1,mount:t=>a(t,(t,o,e,n,r,c)=>s(t,o,e,n,{ditherStrength:c.ditherStrength,fontSize:c.fontSize,chars:c.chars,lightMode:c.lightMode,color:c.ink,accentColor:c.accent}))};export{s as renderSilkBackground,f as silk};//# sourceMappingURL=chunk-3LJ5C3U3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{scenePreset as e}from"./chunk-
|
|
1
|
+
import{scenePreset as e}from"./chunk-3VEMKDBW.js";var n=e({id:"constellation",name:"Constellation",description:"Points wander and neighbours close enough are linked by fine lines that come and go.",cellSize:11,scene:"constellation",charset:" .·:+*#",hover:"lantern",tune:()=>({blend:"off"})});export{n as constellation};//# sourceMappingURL=chunk-3MHEDZLZ.js.map
|