asciify-engine 4.9.0 → 4.11.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/README.md +30 -0
- package/dist/animation-presets-DhswE0D_.d.cts +105 -0
- package/dist/animation-presets-ZujFF3i8.d.ts +105 -0
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +4 -4
- package/dist/core.d.ts +4 -4
- package/dist/core.js +1 -1
- package/dist/field.cjs +1 -0
- package/dist/field.d.cts +183 -0
- package/dist/field.d.ts +183 -0
- package/dist/field.js +1 -0
- package/dist/{hover-catalog-BeIkNwhH.d.cts → hover-catalog-D85jh6dR.d.cts} +40 -1
- package/dist/{hover-catalog-BeIkNwhH.d.ts → hover-catalog-D85jh6dR.d.ts} +40 -1
- package/dist/hover.cjs +1 -1
- package/dist/hover.d.cts +4 -3
- package/dist/hover.d.ts +4 -3
- package/dist/hover.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/{model-ChcdXIzJ.d.ts → model-BVeBDUa9.d.ts} +1 -1
- package/dist/{model-fv_acoD_.d.cts → model-DQNOpbbG.d.cts} +1 -1
- package/dist/studio.cjs +1 -1
- package/dist/studio.d.cts +4 -3
- package/dist/studio.d.ts +4 -3
- package/dist/studio.js +1 -1
- package/dist/{types-C05AfrjT.d.ts → types-CM4452nD.d.ts} +1 -1
- package/dist/{types-D38BMBRB.d.cts → types-FkqhgDYx.d.cts} +1 -1
- package/examples/glyph-field.ts +44 -0
- package/examples/living-image.ts +15 -0
- package/package.json +20 -4
- package/skills/asciify-engine/SKILL.md +53 -14
- package/skills/asciify-engine/agents/openai.yaml +1 -1
- package/skills/asciify-engine/references/design-matching.md +179 -0
- package/skills/asciify-engine/references/glyph-field.md +75 -0
- package/skills/asciify-engine/references/project-integration.md +5 -1
- package/skills/asciify-engine/references/studio-effects.md +29 -0
- package/skills/asciify-engine/references/studio-workspace.md +5 -1
- package/skills/asciify-engine/references/upgrading.md +4 -0
package/dist/field.d.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/** Pure option handling for the GPU glyph field, kept free of DOM and WebGL so it can be tested in Node. */
|
|
2
|
+
type FieldColor = 'ink' | 'source';
|
|
3
|
+
type FieldFit = 'cover' | 'contain';
|
|
4
|
+
/** Generated motion used when there is no media source. */
|
|
5
|
+
type FieldScene = 'none' | 'flow' | 'orb';
|
|
6
|
+
/**
|
|
7
|
+
* How the field answers the pointer.
|
|
8
|
+
* - `fluid`: an incompressible fluid that bends the image and swirls the characters. Bold, liquid.
|
|
9
|
+
* - `lantern`: a soft light that glides after the pointer and lifts the characters it passes. Subtle, no distortion.
|
|
10
|
+
* - `trail`: the Asciify Trail. The same ink field as the studio's Trail; the wake folds each character's tone, then settles.
|
|
11
|
+
* - `none`: still under the pointer.
|
|
12
|
+
*/
|
|
13
|
+
type FieldHover = 'fluid' | 'lantern' | 'trail' | 'none';
|
|
14
|
+
declare const FIELD_HOVERS: readonly {
|
|
15
|
+
value: FieldHover;
|
|
16
|
+
label: string;
|
|
17
|
+
description: string;
|
|
18
|
+
}[];
|
|
19
|
+
interface FieldLanternOptions {
|
|
20
|
+
/** Light radius as a fraction of the field height. */
|
|
21
|
+
radius: number;
|
|
22
|
+
/** How far the light lifts characters toward denser glyphs. */
|
|
23
|
+
strength: number;
|
|
24
|
+
/** Seconds the light's tail takes to fade. 0 is no tail. */
|
|
25
|
+
trail: number;
|
|
26
|
+
/** How quickly the light catches the pointer; higher follows more tightly. */
|
|
27
|
+
follow: number;
|
|
28
|
+
}
|
|
29
|
+
declare const DEFAULT_FIELD_LANTERN: FieldLanternOptions;
|
|
30
|
+
/** Matches the studio's Trail hover settings, so a look moves between renderers unchanged. */
|
|
31
|
+
interface FieldTrailOptions {
|
|
32
|
+
/** 0–1. How far the wake folds tone. */
|
|
33
|
+
strength: number;
|
|
34
|
+
/** 0.1–1. Wake width. */
|
|
35
|
+
radius: number;
|
|
36
|
+
}
|
|
37
|
+
declare const DEFAULT_FIELD_TRAIL: FieldTrailOptions;
|
|
38
|
+
interface FieldFluidOptions {
|
|
39
|
+
/** Brush radius as a fraction of the field height. */
|
|
40
|
+
radius: number;
|
|
41
|
+
/** How strongly pointer speed pushes the fluid. */
|
|
42
|
+
force: number;
|
|
43
|
+
/** Velocity decay per second; higher settles faster. */
|
|
44
|
+
dissipation: number;
|
|
45
|
+
/** How far the fluid bends the sampled image, in field heights per unit of velocity. */
|
|
46
|
+
refraction: number;
|
|
47
|
+
/** How much a wake lifts characters toward denser glyphs and the highlight color. */
|
|
48
|
+
glow: number;
|
|
49
|
+
/** Longest side of the simulation grid in cells. */
|
|
50
|
+
resolution: number;
|
|
51
|
+
/** Vorticity: how strongly the wake curls into eddies. 0 is a straight smear. */
|
|
52
|
+
curl: number;
|
|
53
|
+
}
|
|
54
|
+
interface GlyphFieldSettings {
|
|
55
|
+
/** Ordered from empty to dense. Any graphemes, including words such as " .:-+ASCIIFY". */
|
|
56
|
+
charset: string;
|
|
57
|
+
/** Cell height in CSS pixels. */
|
|
58
|
+
cellSize: number;
|
|
59
|
+
/** Cell width divided by height. 1 is a square grid. */
|
|
60
|
+
cellAspect: number;
|
|
61
|
+
font: string;
|
|
62
|
+
fontWeight: number;
|
|
63
|
+
/** Glyph height relative to the cell. */
|
|
64
|
+
glyphScale: number;
|
|
65
|
+
color: FieldColor;
|
|
66
|
+
ink: string;
|
|
67
|
+
/** Color the wake lifts characters toward. 'auto' brightens each character's own color, so the wake never turns white. */
|
|
68
|
+
highlight: string;
|
|
69
|
+
/** Any CSS hex color, or "transparent". */
|
|
70
|
+
background: string;
|
|
71
|
+
brightness: number;
|
|
72
|
+
contrast: number;
|
|
73
|
+
gamma: number;
|
|
74
|
+
invert: boolean;
|
|
75
|
+
/** Stretch the source's tonal range so the whole charset is used. */
|
|
76
|
+
levels: 'auto' | 'none';
|
|
77
|
+
/** Glyph color saturation in source color mode. */
|
|
78
|
+
saturation: number;
|
|
79
|
+
/** How far darker tones dim the ink, so same-density glyphs such as words still carry the image. 0 is one flat ink. */
|
|
80
|
+
shade: number;
|
|
81
|
+
/** Opacity of a soft, blurred copy of the source behind the characters. 0 draws characters only. */
|
|
82
|
+
backdrop: number;
|
|
83
|
+
fit: FieldFit;
|
|
84
|
+
/** Crop focus for cover, 0–1 on each axis. */
|
|
85
|
+
focus: {
|
|
86
|
+
x: number;
|
|
87
|
+
y: number;
|
|
88
|
+
};
|
|
89
|
+
vignette: number;
|
|
90
|
+
/** Slow ambient sampling drift that keeps a still image alive. 0 is still. */
|
|
91
|
+
drift: number;
|
|
92
|
+
scene: FieldScene;
|
|
93
|
+
/** Speed of generated scenes and drift. */
|
|
94
|
+
speed: number;
|
|
95
|
+
/** Which pointer response runs. */
|
|
96
|
+
hover: FieldHover;
|
|
97
|
+
/** Settings for the fluid hover. `false` turns it off. */
|
|
98
|
+
fluid: FieldFluidOptions | false;
|
|
99
|
+
/** Settings for the lantern hover. */
|
|
100
|
+
lantern: FieldLanternOptions;
|
|
101
|
+
/** Settings for the trail hover. */
|
|
102
|
+
trail: FieldTrailOptions;
|
|
103
|
+
maxPixelRatio: number;
|
|
104
|
+
/** Stop ambient motion and soften the wake when the visitor prefers reduced motion. */
|
|
105
|
+
respectReducedMotion: boolean;
|
|
106
|
+
}
|
|
107
|
+
declare const DEFAULT_FIELD_FLUID: FieldFluidOptions;
|
|
108
|
+
declare const DEFAULT_FIELD_SETTINGS: GlyphFieldSettings;
|
|
109
|
+
type GlyphFieldInput = Partial<Omit<GlyphFieldSettings, 'fluid' | 'focus' | 'lantern' | 'trail'>> & {
|
|
110
|
+
trail?: Partial<FieldTrailOptions>;
|
|
111
|
+
fluid?: Partial<FieldFluidOptions> | boolean;
|
|
112
|
+
lantern?: Partial<FieldLanternOptions>;
|
|
113
|
+
focus?: Partial<GlyphFieldSettings['focus']>;
|
|
114
|
+
};
|
|
115
|
+
/** Splits a charset into graphemes so emoji, block elements and accented letters each take one cell. */
|
|
116
|
+
declare function fieldGlyphs(charset: string): string[];
|
|
117
|
+
declare function normalizeFieldSettings(input?: GlyphFieldInput, base?: GlyphFieldSettings): GlyphFieldSettings;
|
|
118
|
+
/** Maps field UV (0–1, top left) to source UV as `source = uv * scale + offset`. */
|
|
119
|
+
declare function fieldSourceRect(fieldWidth: number, fieldHeight: number, sourceWidth: number, sourceHeight: number, fit: FieldFit, focus?: {
|
|
120
|
+
x: number;
|
|
121
|
+
y: number;
|
|
122
|
+
}): {
|
|
123
|
+
scale: [number, number];
|
|
124
|
+
offset: [number, number];
|
|
125
|
+
};
|
|
126
|
+
/** Device-pixel cell and atlas layout. Cells are whole pixels so glyphs are sampled texel for texel. */
|
|
127
|
+
declare function fieldCellLayout(cellSize: number, cellAspect: number, pixelRatio: number, glyphCount: number): {
|
|
128
|
+
width: number;
|
|
129
|
+
height: number;
|
|
130
|
+
columns: number;
|
|
131
|
+
rows: number;
|
|
132
|
+
atlasWidth: number;
|
|
133
|
+
atlasHeight: number;
|
|
134
|
+
};
|
|
135
|
+
/** 2nd and 98th luminance percentiles of RGBA pixels, used to stretch a source across the charset. */
|
|
136
|
+
declare function fieldLevels(pixels: Uint8ClampedArray | Uint8Array): [number, number];
|
|
137
|
+
/** The hover that actually runs: fluid needs its settings, and anything is off without a pointer response. */
|
|
138
|
+
declare const activeFieldHover: (settings: GlyphFieldSettings) => FieldHover;
|
|
139
|
+
|
|
140
|
+
type FieldSource = string | HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | ImageBitmap;
|
|
141
|
+
interface GlyphFieldOptions extends GlyphFieldInput {
|
|
142
|
+
/** Image or video URL, or a decoded element. Without one, `scene` draws generated motion. */
|
|
143
|
+
source?: FieldSource | null;
|
|
144
|
+
/** Where pointer movement is read. Defaults to the window, so overlaid text never blocks the wake. */
|
|
145
|
+
interactionTarget?: HTMLElement | Window | null;
|
|
146
|
+
/** Accessible name for the canvas. */
|
|
147
|
+
label?: string;
|
|
148
|
+
onError?: (error: Error) => void;
|
|
149
|
+
}
|
|
150
|
+
interface GlyphField {
|
|
151
|
+
readonly canvas: HTMLCanvasElement;
|
|
152
|
+
/** Resolves once the first frame with the current source has been drawn. */
|
|
153
|
+
readonly ready: Promise<void>;
|
|
154
|
+
/** Merges settings; only the parts that changed are rebuilt. */
|
|
155
|
+
update(options: GlyphFieldOptions): void;
|
|
156
|
+
setSource(source: FieldSource | null): Promise<void>;
|
|
157
|
+
/** Moves the wake programmatically, in CSS pixels relative to the canvas. `null` lifts the pointer. */
|
|
158
|
+
pointer(x: number | null, y?: number): void;
|
|
159
|
+
/** Draws the current frame now, so the canvas can be read with drawImage in the same task. */
|
|
160
|
+
redraw(): void;
|
|
161
|
+
play(): void;
|
|
162
|
+
pause(): void;
|
|
163
|
+
resize(): void;
|
|
164
|
+
destroy(): void;
|
|
165
|
+
readonly settings: GlyphFieldSettings;
|
|
166
|
+
}
|
|
167
|
+
/** 'gpu', 'software' (WebGL2 emulated on the CPU, such as SwiftShader or llvmpipe) or 'none'. Probed once per page. */
|
|
168
|
+
declare function glyphFieldTier(): 'gpu' | 'software' | 'none';
|
|
169
|
+
/**
|
|
170
|
+
* True when WebGL2 runs on a GPU. Software WebGL draws every pixel on the CPU and would starve the rest of the page,
|
|
171
|
+
* so it reports false unless `allowSoftware` is set. Mounting can still fail on a blocked GPU, so handle errors.
|
|
172
|
+
*/
|
|
173
|
+
declare function isGlyphFieldSupported(options?: {
|
|
174
|
+
allowSoftware?: boolean;
|
|
175
|
+
}): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* A GPU character field: every cell samples the source once and draws its glyph texel for texel,
|
|
178
|
+
* while a small advected fluid bends the sampling and lifts characters under the cursor.
|
|
179
|
+
* Idle fields stop drawing until the pointer, a video frame or generated motion needs a new frame.
|
|
180
|
+
*/
|
|
181
|
+
declare function mountGlyphField(canvas: HTMLCanvasElement, options?: GlyphFieldOptions): GlyphField;
|
|
182
|
+
|
|
183
|
+
export { DEFAULT_FIELD_FLUID, DEFAULT_FIELD_LANTERN, DEFAULT_FIELD_SETTINGS, DEFAULT_FIELD_TRAIL, FIELD_HOVERS, type FieldColor, type FieldFit, type FieldFluidOptions, type FieldHover, type FieldLanternOptions, type FieldScene, type FieldSource, type FieldTrailOptions, type GlyphField, type GlyphFieldInput, type GlyphFieldOptions, type GlyphFieldSettings, activeFieldHover, fieldCellLayout, fieldGlyphs, fieldLevels, fieldSourceRect, glyphFieldTier, isGlyphFieldSupported, mountGlyphField, normalizeFieldSettings };
|
package/dist/field.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=e=>e?"\nconst float VSCALE=6.;\nvec4 decodeFluid(vec4 t){return vec4((t.xy*2.-1.)*VSCALE,t.z,t.w);}\nvec4 encodeFluid(vec4 v){return vec4(clamp(v.xy/VSCALE*.5+.5,0.,1.),clamp(v.z,0.,1.),1.);}":"\nvec4 decodeFluid(vec4 t){return t;}\nvec4 encodeFluid(vec4 v){return vec4(v.xyz,1.);}",t="#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform vec2 uTexel;\nout vec4 outColor;\nvec2 here(){return gl_FragCoord.xy*uTexel;}\n",n=t+"\nuniform sampler2D uVelocity;\nuniform vec2 uFrom, uTo, uForce;\nuniform float uAspect, uRadius;\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=here(),scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n outColor=vec4(texture(uVelocity,uv).xy+uForce*exp(-d*d/(uRadius*uRadius)),0.,1.);\n}",r=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).y,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).y;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).x,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(.5*(r-l-t+b),0.,0.,1.);\n}",i=t+"\nuniform sampler2D uVelocity, uCurl;\nuniform float uStrength, uDt;\nvoid main(){\n vec2 uv=here();\n float l=texture(uCurl,uv-vec2(uTexel.x,0.)).x,r=texture(uCurl,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uCurl,uv+vec2(0.,uTexel.y)).x,b=texture(uCurl,uv-vec2(0.,uTexel.y)).x,c=texture(uCurl,uv).x;\n vec2 force=.5*vec2(abs(t)-abs(b),abs(r)-abs(l));\n force=force/(length(force)+1e-4)*uStrength*c;\n force.y=-force.y;\n outColor=vec4(clamp(texture(uVelocity,uv).xy+force*uDt,-1000.,1000.),0.,1.);\n}",o=t+"\nuniform sampler2D uVelocity;\nvoid main(){\n vec2 uv=here(),c=texture(uVelocity,uv).xy;\n float l=texture(uVelocity,uv-vec2(uTexel.x,0.)).x,r=texture(uVelocity,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uVelocity,uv+vec2(0.,uTexel.y)).y,b=texture(uVelocity,uv-vec2(0.,uTexel.y)).y;\n if(uv.x-uTexel.x<0.)l=-c.x;\n if(uv.x+uTexel.x>1.)r=-c.x;\n if(uv.y+uTexel.y>1.)t=-c.y;\n if(uv.y-uTexel.y<0.)b=-c.y;\n outColor=vec4(.5*(r-l+t-b),0.,0.,1.);\n}",u=t+"\nuniform sampler2D uSource;\nuniform float uAmount;\nvoid main(){outColor=vec4(texture(uSource,here()).x*uAmount,0.,0.,1.);}",a=t+"\nuniform sampler2D uPressure, uDivergence;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4((l+r+b+t-texture(uDivergence,uv).x)*.25,0.,0.,1.);\n}",l=t+"\nuniform sampler2D uPressure, uVelocity;\nvoid main(){\n vec2 uv=here();\n float l=texture(uPressure,uv-vec2(uTexel.x,0.)).x,r=texture(uPressure,uv+vec2(uTexel.x,0.)).x;\n float t=texture(uPressure,uv+vec2(0.,uTexel.y)).x,b=texture(uPressure,uv-vec2(0.,uTexel.y)).x;\n outColor=vec4(texture(uVelocity,uv).xy-vec2(r-l,t-b),0.,1.);\n}",s=t+"\nuniform sampler2D uVelocity;\nuniform float uDt, uDissipation;\nvoid main(){\n vec2 uv=here();\n vec2 back=uv-uDt*texture(uVelocity,uv).xy*uTexel;\n outColor=vec4(texture(uVelocity,back).xy/(1.+uDissipation*uDt),0.,1.);\n}";var c=class{constructor(e,t){this.previous=null,this.peak=0,this.width=e,this.height=t;const n=e*t;this.pixels=new Uint8Array(4*n),this.signal=new Float32Array(n),this.offsetX=new Float32Array(n),this.offsetY=new Float32Array(n),this.driftX=new Float32Array(n),this.driftY=new Float32Array(n),this.u=new Float32Array(n),this.v=new Float32Array(n),this.ink=new Float32Array(n),this.a=new Float32Array(n),this.b=new Float32Array(n),this.c=new Float32Array(n),this.pressure=new Float32Array(n),this.pressureNext=new Float32Array(n),this.divergence=new Float32Array(n),this.curl=new Float32Array(n)}get active(){return this.peak>2e-4}clear(){for(const e of[this.u,this.v,this.ink,this.a,this.b,this.c,this.pressure,this.pressureNext,this.divergence,this.curl,this.signal,this.pixels,this.offsetX,this.offsetY,this.driftX,this.driftY])e.fill(0);this.previous=null,this.peak=0}leave(){this.previous=null}move(e,t,n,r=performance.now()){if(!Number.isFinite(e+t+n+r))return;e=Math.max(0,Math.min(1,e)),t=Math.max(0,Math.min(1,t)),n=Math.max(.1,Math.min(1,n));const i=this.previous;if(this.previous={x:e,y:t,time:r},!i)return;const o=(e-i.x)*(this.width-1),u=(t-i.y)*(this.height-1),a=Math.hypot(o,u);if(a<.001)return;const l=Math.min(this.width,this.height),s=r>i.time?Math.max(1/240,(r-i.time)/1e3):1/60,c=Math.min(12,a/l/s),h=1-Math.exp(-c/2.2),f=l*(.022+.075*n)*(.65+.95*h),d=Math.min(128,Math.max(1,Math.ceil(a/(.4*f)))),m=a/f*(.32+.22*h)/d,v=o/a,g=u/a,p=-g,x=v,E=a*(2+18*h)/d;for(let n=0;n<d;n++){const r=(n+.5)/d,o=(i.x+(e-i.x)*r)*(this.width-1),u=(i.y+(t-i.y)*r)*(this.height-1),a=Math.max(1,Math.floor(o-3*f)),l=Math.min(this.width-2,Math.ceil(o+3*f)),s=Math.max(1,Math.floor(u-3*f)),c=Math.min(this.height-2,Math.ceil(u+3*f));for(let e=s;e<=c;e++)for(let t=a;t<=l;t++){const n=((t-o)*v+(e-u)*g)/f,r=((t-o)*p+(e-u)*x)/f,i=Math.exp(-n*n*.65-r*r*1.7),a=e*this.width+t;this.ink[a]=1-(1-this.ink[a])*Math.exp(-i*m);const l=1-r*r*.55,s=n*r*.7;this.u[a]=Math.max(-80,Math.min(80,this.u[a]+(v*l+p*s)*i*E)),this.v[a]=Math.max(-80,Math.min(80,this.v[a]+(g*l+x*s)*i*E))}}this.peak=1}read(e,t,n){const r=0|(t=Math.max(.5,Math.min(this.width-1.5,t))),i=0|(n=Math.max(.5,Math.min(this.height-1.5,n))),o=t-r,u=n-i,a=i*this.width+r;return(e[a]*(1-o)+e[a+1]*o)*(1-u)+(e[a+this.width]*(1-o)+e[a+this.width+1]*o)*u}sample(e,t){return this.read(this.signal,e*(this.width-1),t*(this.height-1))}displacement(e,t,n){n[0]=this.read(this.offsetX,e*(this.width-1),t*(this.height-1)),n[1]=this.read(this.offsetY,e*(this.width-1),t*(this.height-1))}step(e){if(!this.active||!Number.isFinite(e))return;let t=Math.max(0,Math.min(.05,e));const n=this.width,r=this.height;for(;t>1e-7;){const e=Math.min(1/60,t);t-=e;const i=Math.exp(-1.9*e),o=Math.exp(-3.2*e);for(let t=1;t<r-1;t++)for(let u=1;u<n-1;u++){const a=t*n+u,l=Math.max(.5,Math.min(n-1.5,u-this.u[a]*e)),s=Math.max(.5,Math.min(r-1.5,t-this.v[a]*e)),c=0|l,h=0|s,f=l-c,d=s-h,m=h*n+c,v=(1-f)*(1-d),g=f*(1-d),p=(1-f)*d,x=f*d;this.a[a]=(this.u[m]*v+this.u[m+1]*g+this.u[m+n]*p+this.u[m+n+1]*x)*i,this.b[a]=(this.v[m]*v+this.v[m+1]*g+this.v[m+n]*p+this.v[m+n+1]*x)*i,this.c[a]=(this.ink[m]*v+this.ink[m+1]*g+this.ink[m+n]*p+this.ink[m+n+1]*x)*o}[this.u,this.a]=[this.a,this.u],[this.v,this.b]=[this.b,this.v],[this.ink,this.c]=[this.c,this.ink];for(let e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.curl[r]=.5*(this.v[r+1]-this.v[r-1]-this.u[r+n]+this.u[r-n])}for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r,o=Math.abs(this.curl[i+1])-Math.abs(this.curl[i-1]),u=Math.abs(this.curl[i+n])-Math.abs(this.curl[i-n]),a=Math.hypot(o,u)+1e-4,l=this.curl[i]*e*10,s=Math.hypot(this.u[i],this.v[i]);this.u[i]+=u/a*l,this.v[i]-=o/a*l;const c=Math.min(1,s/(Math.hypot(this.u[i],this.v[i])+1e-6));this.u[i]*=c,this.v[i]*=c,this.divergence[i]=-.5*(this.u[i+1]-this.u[i-1]+this.v[i+n]-this.v[i-n])}this.pressure.fill(0);for(let e=0;e<4;e++){for(let e=1;e<r-1;e++)for(let t=1;t<n-1;t++){const r=e*n+t;this.pressureNext[r]=.25*(this.divergence[r]+this.pressure[r-1]+this.pressure[r+1]+this.pressure[r-n]+this.pressure[r+n])}[this.pressure,this.pressureNext]=[this.pressureNext,this.pressure]}let u=0;for(let t=1;t<r-1;t++)for(let r=1;r<n-1;r++){const i=t*n+r;this.u[i]=Math.max(-80,Math.min(80,this.u[i]-.5*(this.pressure[i+1]-this.pressure[i-1]))),this.v[i]=Math.max(-80,Math.min(80,this.v[i]-.5*(this.pressure[i+n]-this.pressure[i-n]))),this.driftX[i]+=(3.6*this.u[i]-58*this.offsetX[i]-12*this.driftX[i])*e,this.driftY[i]+=(3.6*this.v[i]-58*this.offsetY[i]-12*this.driftY[i])*e,this.offsetX[i]=Math.max(-2,Math.min(2,this.offsetX[i]+this.driftX[i]*e)),this.offsetY[i]=Math.max(-2,Math.min(2,this.offsetY[i]+this.driftY[i]*e)),this.signal[i]=1-Math.exp(.06*-Math.hypot(this.u[i],this.v[i])-.16*this.ink[i]),u=Math.max(u,this.signal[i])}this.peak=u}if(this.active)for(let e=0;e<this.ink.length;e++){const t=Math.round(65535*Math.min(1,this.signal[e]));this.pixels[4*e]=t>>>8,this.pixels[4*e+1]=255&t,this.pixels[4*e+2]=Math.round(128+63.5*this.offsetX[e]),this.pixels[4*e+3]=Math.round(128+63.5*this.offsetY[e])}else this.clear()}},h=[{value:"fluid",label:"Fluid",description:"An ink-in-water wake that bends the image and swirls the characters."},{value:"lantern",label:"Lantern",description:"A soft light follows the pointer and lifts the characters it passes, without distortion."},{value:"trail",label:"Trail",description:"The Asciify Trail: in the wake, sparse characters fill in and dense ones open up, then settle. No distortion."},{value:"none",label:"Off",description:"The field stays still under the pointer."}],f={radius:.13,strength:.5,trail:.45,follow:12},d={strength:.55,radius:.45},m={radius:.075,force:1,dissipation:1.6,refraction:.03,glow:.55,resolution:128,curl:24},v={charset:" .,:-=+*#%@",cellSize:12,cellAspect:1,font:'ui-monospace, "SF Mono", Menlo, Consolas, monospace',fontWeight:500,glyphScale:.8,color:"ink",ink:"#f3ba32",highlight:"auto",background:"transparent",brightness:0,contrast:1,gamma:1,invert:!1,levels:"auto",saturation:1.1,shade:.5,backdrop:0,fit:"cover",focus:{x:.5,y:.5},vignette:0,drift:0,scene:"flow",speed:1,hover:"fluid",fluid:m,lantern:f,trail:d,maxPixelRatio:2,respectReducedMotion:!0},g=(e,t,n,r)=>"number"==typeof e&&Number.isFinite(e)?Math.min(n,Math.max(t,e)):r,p=(e,t)=>"string"==typeof e&&/^#([\da-f]{3}|[\da-f]{6})$/i.test(e.trim())?e.trim().toLowerCase():t;function x(e){const t=Intl.Segmenter;return(t?Array.from(new t(void 0,{granularity:"grapheme"}).segment(e),e=>e.segment):Array.from(e)).filter(e=>"\n"!==e&&"\r"!==e&&"\t"!==e).slice(0,256)}function E(e={},t=v){const n="string"==typeof e.charset&&x(e.charset).length>=2?e.charset:t.charset,r=t.fluid||m,i=void 0===e.fluid?t.fluid:e.fluid,o=!1!==i&&(()=>{const e=!0!==i&&i?{...r,...i}:r;return{radius:g(e.radius,.005,.5,m.radius),force:g(e.force,0,8,m.force),dissipation:g(e.dissipation,.05,20,m.dissipation),refraction:g(e.refraction,0,.5,m.refraction),glow:g(e.glow,0,2,m.glow),resolution:Math.round(g(e.resolution,16,512,m.resolution)),curl:g(e.curl,0,80,m.curl)}})(),u="none"===e.scene||"flow"===e.scene||"orb"===e.scene?e.scene:t.scene;return{charset:n,cellSize:g(e.cellSize,3,96,t.cellSize),cellAspect:g(e.cellAspect,.3,2,t.cellAspect),font:"string"==typeof e.font&&e.font.trim()?e.font:t.font,fontWeight:Math.round(g(e.fontWeight,100,900,t.fontWeight)),glyphScale:g(e.glyphScale,.3,1.6,t.glyphScale),color:"source"===e.color||"ink"===e.color?e.color:t.color,ink:p(e.ink,t.ink),highlight:"auto"===e.highlight?"auto":p(e.highlight,t.highlight),background:"transparent"===e.background?"transparent":p(e.background,t.background),brightness:g(e.brightness,-1,1,t.brightness),contrast:g(e.contrast,0,4,t.contrast),gamma:g(e.gamma,.2,5,t.gamma),invert:"boolean"==typeof e.invert?e.invert:t.invert,levels:"none"===e.levels||"auto"===e.levels?e.levels:t.levels,saturation:g(e.saturation,0,3,t.saturation),shade:g(e.shade,0,1,t.shade),backdrop:g(e.backdrop,0,1,t.backdrop),fit:"contain"===e.fit||"cover"===e.fit?e.fit:t.fit,focus:{x:g(e.focus?.x,0,1,t.focus.x),y:g(e.focus?.y,0,1,t.focus.y)},vignette:g(e.vignette,0,1,t.vignette),drift:g(e.drift,0,2,t.drift),scene:u,speed:g(e.speed,0,5,t.speed),hover:"fluid"===e.hover||"lantern"===e.hover||"trail"===e.hover||"none"===e.hover?e.hover:t.hover,fluid:o,lantern:{radius:g(e.lantern?.radius,.02,.6,t.lantern.radius),strength:g(e.lantern?.strength,0,1.5,t.lantern.strength),trail:g(e.lantern?.trail,0,3,t.lantern.trail),follow:g(e.lantern?.follow,1,60,t.lantern.follow)},trail:{strength:g(e.trail?.strength,0,1,t.trail.strength),radius:g(e.trail?.radius,.1,1,t.trail.radius)},maxPixelRatio:g(e.maxPixelRatio,.5,4,t.maxPixelRatio),respectReducedMotion:"boolean"==typeof e.respectReducedMotion?e.respectReducedMotion:t.respectReducedMotion}}function T(e,t,n,r,i,o={x:.5,y:.5}){if(!(e>0&&t>0&&n>0&&r>0))return{scale:[1,1],offset:[0,0]};const u=e/t,a=n/r,l=a>u,s="cover"===i?l?[u/a,1]:[1,a/u]:l?[1,a/u]:[u/a,1];return{scale:s,offset:"cover"===i?[(1-s[0])*o.x,(1-s[1])*o.y]:[.5*(1-s[0]),.5*(1-s[1])]}}function y(e,t,n,r){const i=Math.max(2,Math.round(e*n)),o=Math.max(2,Math.round(i*t)),u=Math.min(16,Math.max(1,r)),a=Math.ceil(r/u);return{width:o,height:i,columns:u,rows:a,atlasWidth:u*o,atlasHeight:a*i}}function b(e){const t=e.replace("#",""),n=3===t.length?t.split("").map(e=>e+e).join(""):t,r=Number.parseInt(n,16);return Number.isFinite(r)&&6===n.length?[(r>>16&255)/255,(r>>8&255)/255,(255&r)/255]:[0,0,0]}function M(e){const t=new Uint32Array(256);let n=0;for(let r=0;r+3<e.length;r+=4)e[r+3]<8||(t[Math.round(.2126*e[r]+.7152*e[r+1]+.0722*e[r+2])]++,n++);if(!n)return[0,1];const r=e=>{let r=0;for(let i=0;i<256;i++)if(r+=t[i],r>=n*e)return i/255;return 1},i=r(.02),o=r(.98);return o-i<.08?[0,1]:[i,o]}var w,R=e=>"fluid"!==e.hover||e.fluid?e.hover:"none",A=/\.(mp4|webm|mov|m4v|ogv)(\?|#|$)/i,F=/swiftshader|llvmpipe|softpipe|software|basic render/i;function L(){if(w)return w;if("undefined"==typeof document||void 0===globalThis.WebGL2RenderingContext)return w="none";try{const e=document.createElement("canvas").getContext("webgl2");if(!e)return w="none";const t=e.getExtension("WEBGL_debug_renderer_info"),n=String(e.getParameter(t?t.UNMASKED_RENDERER_WEBGL:e.RENDERER)??"");return e.getExtension("WEBGL_lose_context")?.loseContext(),w=F.test(n)?"software":"gpu"}catch{return w="none"}}function S(e={}){const t=L();return"gpu"===t||"software"===t&&!!e.allowSoftware}function D(t,h={}){let f=E(h);const d=h.onError,m=t.getContext("webgl2",{alpha:!0,antialias:!1,depth:!1,stencil:!1,premultipliedAlpha:!0,powerPreference:"high-performance"});if(!m)throw new Error("WebGL2 is not available for the glyph field.");const v=m;t.setAttribute("role","img"),t.setAttribute("aria-label",h.label??"ASCII artwork"),t.dataset.renderer="glyph-field";let g,p,w,F,L=!1,S={},D={},_=[],k=[],C=null,P=!1,U=null;const B=[];let I=0,V=0,X=1,G=0,N=y(f.cellSize,f.cellAspect,1,2),H=2,z=0,O=0,W=0,Y=null,q=null,$=0,j=0,K=0,J=!0,Q=0,Z=[0,1],ee=-1/0,te=0,ne=0,re=0,ie=!0,oe=!1,ue=!1,ae=!1,le=!0,se=null,ce=null,he=1/60,fe=0,de=0;const me={target:null,at:null,strength:0,trail:[]},ve=new Float32Array(36);let ge=null,pe=null,xe=!1;const Ee=()=>{if(!I||!V)return null;const e=I/V,t=Math.round(e>=1?128:128*e),n=Math.round(e>=1?128/e:128);return ge&&ge.width===t&&ge.height===n||(ge=new c(t,n),pe||(pe=Le(v.LINEAR)),v.bindTexture(v.TEXTURE_2D,pe),v.texImage2D(v.TEXTURE_2D,0,v.RGBA8,t,n,0,v.RGBA,v.UNSIGNED_BYTE,ge.pixels),xe=!0),ge};let Te=0;const ye="function"==typeof matchMedia?matchMedia("(prefers-reduced-motion: reduce)"):null,be=()=>f.respectReducedMotion&&!!ye?.matches;let Me;const we=new Promise(e=>{Me=e});let Re=!0;const Ae=(e,t)=>{const n=v.createShader(e);if(B.push(n),v.shaderSource(n,t),v.compileShader(n),!v.getShaderParameter(n,v.COMPILE_STATUS))throw new Error(v.getShaderInfoLog(n)||"Glyph field shader failed to compile.");return n},Fe=(e,t)=>{const n=v.createProgram();if(v.attachShader(n,Ae(v.VERTEX_SHADER,"#version 300 es\nvoid main(){vec2 p=vec2((gl_VertexID<<1)&2,gl_VertexID&2);gl_Position=vec4(p*2.-1.,0.,1.);}")),v.attachShader(n,Ae(v.FRAGMENT_SHADER,e)),v.linkProgram(n),!v.getProgramParameter(n,v.LINK_STATUS))throw new Error(v.getProgramInfoLog(n)||"Glyph field program failed to link.");return{program:n,at:Object.fromEntries(t.map(e=>[e,v.getUniformLocation(n,e)]))}},Le=e=>{const t=v.createTexture();return v.bindTexture(v.TEXTURE_2D,t),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MIN_FILTER,e),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_MAG_FILTER,e===v.LINEAR_MIPMAP_LINEAR?v.LINEAR:e),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_WRAP_S,v.CLAMP_TO_EDGE),v.texParameteri(v.TEXTURE_2D,v.TEXTURE_WRAP_T,v.CLAMP_TO_EDGE),t},Se=()=>{C?.release(),P=!1,_.forEach(e=>v.deleteTexture(e)),k.forEach(e=>v.deleteFramebuffer(e)),_=[],k=[]},De=()=>{if(Se(),!f.fluid||!I||!V)return;const e=f.fluid.resolution,t=I/V;if(z=t>=1?e:Math.max(8,Math.round(e*t)),O=t>=1?Math.max(8,Math.round(e/t)):e,P=!!C&&C.resize(z,O),P)return void(W=0);const n=()=>{Se();for(let e=0;e<2;e++){const e=Le(v.LINEAR);v.texImage2D(v.TEXTURE_2D,0,L?v.RGBA8:v.RGBA16F,z,O,0,v.RGBA,L?v.UNSIGNED_BYTE:v.HALF_FLOAT,null);const t=v.createFramebuffer();v.bindFramebuffer(v.FRAMEBUFFER,t),v.framebufferTexture2D(v.FRAMEBUFFER,v.COLOR_ATTACHMENT0,v.TEXTURE_2D,e,0),_.push(e),k.push(t)}return v.checkFramebufferStatus(v.FRAMEBUFFER)===v.FRAMEBUFFER_COMPLETE};if(!n()){if(L)throw new Error("The glyph field could not allocate its fluid buffers.");L=!0,_e(),n()}for(const e of k)v.bindFramebuffer(v.FRAMEBUFFER,e),v.viewport(0,0,z,O),v.clearColor(L?.5:0,L?.5:0,0,1),v.clear(v.COLOR_BUFFER_BIT);v.bindFramebuffer(v.FRAMEBUFFER,null),W=0};function _e(){g&&v.deleteProgram(g),p&&v.deleteProgram(p);const t=Fe((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uSource, uAtlas, uFluid;\nuniform vec2 uResolution, uCell;\nuniform float uCount, uColumns;\nuniform vec4 uSourceMap;\nuniform int uScene;\nuniform vec3 uInk, uHighlight;\nuniform vec4 uBackground;\nuniform float uBrightness, uContrast, uGamma, uInvert, uVignette, uSourceColor;\nuniform float uRefraction, uGlow, uTime, uDrift, uFluidOn, uLod, uSaturation, uBackdrop, uShade, uHighlightAuto, uFluidMode;\nuniform vec2 uFluidTexel;\nuniform int uHoverMode;\nuniform vec3 uLight;\nuniform vec3 uTrail[12];\nuniform float uLightRadius, uLightGain, uClock, uFlicker, uTrailAmount;\nuniform sampler2D uTrailMap;\nuniform vec2 uLevels;\nout vec4 outColor;\n${e(t)}\nfloat hash(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}\nfloat noise(vec2 p){vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);\n return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),u.x),u.y);}\nfloat fbm(vec2 p){float v=0.,a=.5;for(int i=0;i<5;i++){v+=a*noise(p);p=p*2.03+vec2(1.7,9.2);a*=.5;}return v;}\nvec3 generated(vec2 uv,float aspect){\n vec2 p=(uv-.5)*vec2(aspect,1.);\n if(uScene==1){\n float t=uTime*.08;\n vec2 q=vec2(fbm(p*1.6+t),fbm(p*1.6-t+3.1));\n return vec3(smoothstep(.28,.86,fbm(p*1.8+q*1.6+vec2(t*.6,-t*.4))));\n }\n if(uScene==2){\n float r=.36,d=length(p),a=uTime*.25;\n vec3 l=normalize(vec3(cos(a)*.8,.55,sin(a)*.6+.6));\n if(d<r){\n vec3 n=normalize(vec3(p.x,-p.y,sqrt(r*r-d*d)));\n float diffuse=max(dot(n,l),0.),rim=pow(1.-n.z,2.5);\n float lon=atan(n.x,n.z)+uTime*.35,lat=asin(clamp(n.y,-1.,1.));\n float bands=.5+.25*sin(lat*14.)+.25*sin(lon*6.);\n return vec3(clamp(diffuse*.85+rim*.5+bands*.12,0.,1.));\n }\n return vec3(exp(-pow((d-r)*9.,2.))*.35);\n }\n return vec3(0.);\n}\nvoid main(){\n vec2 frag=vec2(gl_FragCoord.x,uResolution.y-gl_FragCoord.y);\n vec2 cell=floor(frag/uCell);\n vec2 uv=(cell+.5)*uCell/uResolution;\n float aspect=uResolution.x/uResolution.y;\n vec4 fluid=vec4(0.);\n // lift: signed tone change. The solver's flow brightens one side of each eddy and opens the other into gaps.\n float lift=0.,wake=0.,bend=1.,swirl=0.,speed=0.;\n if(uFluidOn>.5){\n vec4 raw=texture(uFluid,vec2(uv.x,1.-uv.y));\n if(uFluidMode>.5){\n vec2 v=raw.xy*uFluidTexel*vec2(aspect,1.);\n fluid=vec4(v,0.,1.);\n swirl=clamp(dot(v,vec2(.4,.65))*uGlow*.32,-.65,.65);\n speed=clamp(length(v)*uGlow*.055,0.,.38);\n bend=.4;\n } else {\n fluid=decodeFluid(raw);\n wake=clamp(fluid.z*uGlow,0.,1.);\n lift=wake*.55;\n }\n }\n vec2 drift=uDrift*.006*vec2(sin(uv.y*5.3+uTime*.6),cos(uv.x*4.1-uTime*.5));\n vec2 sampleAt=uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend+drift;\n vec3 rgb,glow=vec3(0.);\n if(uScene==0){\n vec2 s=sampleAt*uSourceMap.xy+uSourceMap.zw;\n bool inside=!(s.x<0.||s.y<0.||s.x>1.||s.y>1.);\n rgb=inside?textureLod(uSource,s,uLod).rgb:vec3(0.);\n if(uBackdrop>0.){\n vec2 b=(uv-vec2(fluid.x/aspect,-fluid.y)*uRefraction*bend*.5)*uSourceMap.xy+uSourceMap.zw;\n glow=(b.x<0.||b.y<0.||b.x>1.||b.y>1.)?vec3(0.):textureLod(uSource,b,uLod+3.).rgb*uBackdrop;\n }\n } else rgb=generated(sampleAt,aspect);\n float tone=dot(rgb,vec3(.2126,.7152,.0722));\n tone=clamp((tone-uLevels.x)/max(uLevels.y-uLevels.x,.01),0.,1.);\n tone=clamp((tone-.5)*uContrast+.5+uBrightness,0.,1.);\n tone=pow(tone,1./uGamma);\n if(uInvert>.5)tone=1.-tone;\n float flick=0.,trailFold=0.;\n if(uHoverMode==2){\n // Lantern: a soft light and its fading tail lift the characters they pass. Nothing moves the image.\n vec2 ap=vec2(aspect,1.),d=(uv-uLight.xy)*ap;\n float r2=uLightRadius*uLightRadius,glowL=uLight.z*exp(-dot(d,d)/r2);\n for(int i=0;i<12;i++){vec2 e=(uv-uTrail[i].xy)*ap;glowL+=uTrail[i].z*exp(-dot(e,e)/(r2*.55));}\n glowL=min(glowL,1.);\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=glowL*uLightGain*(.7+.6*grain);\n wake=glowL*.4;\n // A few letters at the light's rim step up the ramp and back, as if being retyped.\n flick=uFlicker*step(.9,fract(grain*13.7+floor(uClock*7.+grain*5.)*.37))*glowL*(1.-glowL)*4.;\n }\n if(uHoverMode==3){\n // Trail: the studio's ink field, as a 16-bit density. Sparse marks fill in, dense marks open up.\n vec4 ink=texture(uTrailMap,uv);\n float density=(ink.r*65280.+ink.g*255.)/65535.*uTrailAmount;\n float t=clamp(density/.38,0.,1.);\n trailFold=t*t*(3.-2.*t);\n }\n if(uFluidMode>.5&&uHoverMode==1){\n // Dark ground: any motion lights the characters. Lit ground: the flow lights one side of each eddy and opens the other.\n // A little cell-to-cell grain keeps the lit area a shimmer of letters rather than a solid block.\n float grain=fract(sin(dot(cell,vec2(12.9898,78.233)))*43758.5453);\n lift=mix(abs(swirl)*.5+speed,swirl+speed*.35,smoothstep(.12,.55,tone))*(.65+.7*grain);\n wake=clamp(lift*1.6,0.,1.);\n }\n tone=clamp(tone+lift,0.,1.);\n tone+=(1.-2.*tone)*trailFold;\n tone*=1.-uVignette*smoothstep(.35,1.05,length((uv-.5)*vec2(min(aspect,1.8),1.)*2.));\n float index=min(uCount-1.,floor(tone*(uCount-1.)+.5)+(flick>.5&&tone>.02?1.:0.));\n vec2 local=frag-cell*uCell;\n ivec2 tile=ivec2(int(mod(index,uColumns)),int(floor(index/uColumns)));\n float coverage=texelFetch(uAtlas,tile*ivec2(uCell)+ivec2(local),0).a;\n vec3 hue=rgb/max(max(rgb.r,rgb.g),max(rgb.b,.03));\n hue=mix(vec3(dot(hue,vec3(.2126,.7152,.0722))),hue,uSaturation);\n vec3 ink=mix(uInk*mix(1.,.3+.7*tone,uShade),clamp(hue*(.38+.62*tone),0.,1.),uSourceColor);\n vec3 lifted=uHighlightAuto>.5?ink/max(max(ink.r,ink.g),max(ink.b,.06)):uHighlight;\n ink=mix(ink,lifted,wake*.7);\n float underAlpha=max(uBackground.a,min(1.,max(glow.r,max(glow.g,glow.b))));\n vec3 under=min(uBackground.rgb*uBackground.a+glow,vec3(underAlpha));\n outColor=vec4(ink*coverage+under*(1.-coverage),coverage+underAlpha*(1.-coverage));\n}`)(L),["uSource","uAtlas","uFluid","uResolution","uCell","uCount","uColumns","uSourceMap","uScene","uInk","uHighlight","uBackground","uBrightness","uContrast","uGamma","uInvert","uVignette","uSourceColor","uRefraction","uGlow","uTime","uDrift","uFluidOn","uLod","uSaturation","uBackdrop","uLevels","uShade","uHighlightAuto","uFluidMode","uFluidTexel","uHoverMode","uLight","uTrail","uLightRadius","uLightGain","uClock","uFlicker","uTrailMap","uTrailAmount"]),c=Fe((t=>`#version 300 es\nprecision highp float;\nprecision highp sampler2D;\nuniform sampler2D uPrevious;\nuniform vec2 uSize;\nuniform float uAspect, uDt, uDecay, uEnergyDecay, uRadius, uSplat;\nuniform vec2 uFrom, uTo, uForce;\nout vec4 outColor;\n${e(t)}\nfloat segment(vec2 p,vec2 a,vec2 b){vec2 pa=p-a,ba=b-a;float h=clamp(dot(pa,ba)/max(dot(ba,ba),1e-8),0.,1.);return length(pa-ba*h);}\nvoid main(){\n vec2 uv=gl_FragCoord.xy/uSize;\n vec4 here=decodeFluid(texture(uPrevious,uv));\n vec4 state=decodeFluid(texture(uPrevious,uv-uDt*here.xy*vec2(1./uAspect,1.)));\n state.xy*=exp(-uDt*uDecay);\n state.z*=exp(-uDt*uEnergyDecay);\n if(uSplat>.5){\n vec2 scale=vec2(uAspect,1.);\n float d=segment(uv*scale,uFrom*scale,uTo*scale);\n float g=exp(-d*d/(uRadius*uRadius));\n state.xy+=uForce*g;\n state.z=min(1.,state.z+g*min(1.,length(uForce)*.5+.08));\n }\n float speed=length(state.xy);\n if(speed>5.)state.xy*=5./speed;\n outColor=encodeFluid(state);\n}`)(L),["uPrevious","uSize","uAspect","uDt","uDecay","uEnergyDecay","uRadius","uSplat","uFrom","uTo","uForce"]);g=t.program,S=t.at,p=c.program,D=c.at,C?.dispose(),C=L?null:function(e,t){const c={splat:t(n,["uTexel","uVelocity","uFrom","uTo","uForce","uAspect","uRadius"]),curl:t(r,["uTexel","uVelocity"]),vorticity:t(i,["uTexel","uVelocity","uCurl","uStrength","uDt"]),divergence:t(o,["uTexel","uVelocity"]),fade:t(u,["uTexel","uSource","uAmount"]),pressure:t(a,["uTexel","uPressure","uDivergence"]),gradient:t(l,["uTexel","uPressure","uVelocity"]),advect:t(s,["uTexel","uVelocity","uDt","uDissipation"])};let h=0,f=0,d=[],m=[],v=null,g=null;const p=t=>{const n=e.createTexture();e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texImage2D(e.TEXTURE_2D,0,e.RGBA16F,h,f,0,e.RGBA,e.HALF_FLOAT,null);const r=e.createFramebuffer();return e.bindFramebuffer(e.FRAMEBUFFER,r),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,n,0),e.checkFramebufferStatus(e.FRAMEBUFFER)!==e.FRAMEBUFFER_COMPLETE?(e.deleteFramebuffer(r),e.deleteTexture(n),null):(e.viewport(0,0,h,f),e.clearColor(0,0,0,1),e.clear(e.COLOR_BUFFER_BIT),{texture:n,buffer:r})},x=t=>{t&&(e.deleteTexture(t.texture),e.deleteFramebuffer(t.buffer))},E=()=>{[...d,...m,v,g].forEach(x),d=[],m=[],v=g=null},T=(t,n,r,i={})=>{const{program:o,at:u}=c[t];e.useProgram(o),e.bindFramebuffer(e.FRAMEBUFFER,n.buffer),e.uniform2f(u.uTexel,1/h,1/f);let a=0;for(const[t,n]of Object.entries(r))e.activeTexture(e.TEXTURE0+a),e.bindTexture(e.TEXTURE_2D,n),e.uniform1i(u[t],a++);for(const[t,n]of Object.entries(i))Array.isArray(n)?e.uniform2f(u[t],n[0],n[1]):e.uniform1f(u[t],n);e.drawArrays(e.TRIANGLES,0,3)};return{resize(t,n){E(),h=t,f=n;const r=[p(e.LINEAR),p(e.LINEAR),p(e.NEAREST),p(e.NEAREST),p(e.NEAREST),p(e.NEAREST)];return e.bindFramebuffer(e.FRAMEBUFFER,null),r.some(e=>!e)?(r.forEach(x),!1):(d=[r[0],r[1]],m=[r[2],r[3]],v=r[4],g=r[5],!0)},step(t,n){if(!d.length||!v||!g)return;e.viewport(0,0,h,f);const r=e=>{e.reverse()};t&&(T("splat",d[1],{uVelocity:d[0].texture},{uFrom:t.from,uTo:t.to,uForce:t.force,uAspect:n.aspect,uRadius:n.radius}),r(d)),n.curl>0&&(T("curl",g,{uVelocity:d[0].texture}),T("vorticity",d[1],{uVelocity:d[0].texture,uCurl:g.texture},{uStrength:n.curl,uDt:n.dt}),r(d)),T("divergence",v,{uVelocity:d[0].texture}),T("fade",m[1],{uSource:m[0].texture},{uAmount:.8}),r(m);for(let e=0;e<20;e++)T("pressure",m[1],{uPressure:m[0].texture,uDivergence:v.texture}),r(m);T("gradient",d[1],{uPressure:m[0].texture,uVelocity:d[0].texture}),r(d),T("advect",d[1],{uVelocity:d[0].texture},{uDt:n.dt,uDissipation:n.dissipation}),r(d),e.bindFramebuffer(e.FRAMEBUFFER,null)},get velocity(){return d[0]?.texture??null},get size(){return[h,f]},release:E,dispose(){E(),Object.values(c).forEach(({program:t})=>e.deleteProgram(t))}}}(v,Fe)}const ke=()=>`${f.fontWeight} ${Math.max(1,N.height*f.glyphScale)}px ${f.font}`,Ce=()=>{const e=x(f.charset);H=e.length,N=y(f.cellSize,f.cellAspect,X,H),G=X;const t=document.createElement("canvas");t.width=N.atlasWidth,t.height=N.atlasHeight;const n=t.getContext("2d");n.fillStyle="#fff",n.textAlign="center",n.textBaseline="alphabetic",n.font=ke();const r=n.measureText("M"),i=r.actualBoundingBoxAscent||N.height*f.glyphScale*.7,o=r.actualBoundingBoxDescent||0,u=(N.height+i-o)/2;e.forEach((e,t)=>{const r=t%N.columns*N.width+N.width/2,i=Math.floor(t/N.columns)*N.height+u,o=n.measureText(e).width;n.save(),n.beginPath(),n.rect(r-N.width/2,i-u,N.width,N.height),n.clip(),n.translate(r,i),o>.96*N.width&&n.scale(.96*N.width/o,1),n.fillText(e,0,0),n.restore()}),v.bindTexture(v.TEXTURE_2D,w),v.pixelStorei(v.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),v.texImage2D(v.TEXTURE_2D,0,v.RGBA8,v.RGBA,v.UNSIGNED_BYTE,t),t.width=t.height=1,ie=!0},Pe=()=>{const e=document.fonts;if(!e?.load)return;const t=ke();e.load(t,x(f.charset).join("")).then(()=>{oe||ue||ke()!==t||(Ce(),He())},()=>{})},Ue=()=>{if(Z=[0,1],Y&&"none"!==f.levels)try{const e=document.createElement("canvas");e.width=e.height=64;const t=e.getContext("2d",{willReadFrequently:!0});t.drawImage(Y,0,0,64,64),Z=M(t.getImageData(0,0,64,64).data)}catch{Z=[0,1]}},Be=()=>{if(!Y)return;const e=performance.now();Y instanceof HTMLVideoElement&&!(e-ee>1500)||(Ue(),ee=e),v.bindTexture(v.TEXTURE_2D,F),v.pixelStorei(v.UNPACK_FLIP_Y_WEBGL,!1),v.pixelStorei(v.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1);try{v.texImage2D(v.TEXTURE_2D,0,v.RGBA8,v.RGBA,v.UNSIGNED_BYTE,Y),v.generateMipmap(v.TEXTURE_2D)}catch(e){d?.(e instanceof Error?e:new Error(String(e)))}},Ie=()=>{L=!v.getExtension("EXT_color_buffer_float")&&!v.getExtension("EXT_color_buffer_half_float"),_e(),U=v.createVertexArray(),w=Le(v.NEAREST),F=Le(v.LINEAR_MIPMAP_LINEAR),v.bindTexture(v.TEXTURE_2D,F),v.texImage2D(v.TEXTURE_2D,0,v.RGBA8,1,1,0,v.RGBA,v.UNSIGNED_BYTE,new Uint8Array([0,0,0,255])),v.generateMipmap(v.TEXTURE_2D),I=V=0,Ve(),Ce(),Pe(),De(),Y&&Be()};function Ve(){const e=t.getBoundingClientRect(),n=Math.min(globalThis.devicePixelRatio||1,f.maxPixelRatio),r=Math.max(1,Math.round(e.width*n)),i=Math.max(1,Math.round(e.height*n));if(r===I&&i===V&&n===X)return!1;const o=!I||Math.abs(r/i-I/V)>.01;return X=n,I=r,V=i,t.width=I,t.height=V,w&&G!==X&&Ce(),!p||!o&&_.length||De(),ie=!0,!0}const Xe=()=>{const e=f.fluid,t=T(I,V,$||1,j||1,f.fit,f.focus),n=Y&&j?N.height/V*j*t.scale[1]:1,r="transparent"===f.background?[0,0,0,0]:[...b(f.background),1];v.bindFramebuffer(v.FRAMEBUFFER,null),v.viewport(0,0,I,V),v.useProgram(g),v.activeTexture(v.TEXTURE0),v.bindTexture(v.TEXTURE_2D,F),v.uniform1i(S.uSource,0),v.activeTexture(v.TEXTURE1),v.bindTexture(v.TEXTURE_2D,w),v.uniform1i(S.uAtlas,1),v.activeTexture(v.TEXTURE2),v.bindTexture(v.TEXTURE_2D,(P?C?.velocity:_[W])??F),v.uniform1i(S.uFluid,2),v.uniform1f(S.uFluidMode,P?1:0);const i=R(f);v.uniform1i(S.uHoverMode,"fluid"===i?1:"lantern"===i?2:"trail"===i&&xe?3:0),v.activeTexture(v.TEXTURE3),v.bindTexture(v.TEXTURE_2D,pe??F),v.uniform1i(S.uTrailMap,3),v.uniform1f(S.uTrailAmount,f.trail.strength),v.uniform3f(S.uLight,me.at?.[0]??-1,me.at?.[1]??-1,"lantern"===i?me.strength:0),v.uniform3fv(S.uTrail,ve),v.uniform1f(S.uLightRadius,f.lantern.radius),v.uniform1f(S.uLightGain,f.lantern.strength),v.uniform1f(S.uClock,performance.now()/1e3),v.uniform1f(S.uFlicker,be()?0:1),v.uniform2f(S.uFluidTexel,1/Math.max(1,z),1/Math.max(1,O)),v.uniform2f(S.uResolution,I,V),v.uniform2f(S.uCell,N.width,N.height),v.uniform1f(S.uCount,H),v.uniform1f(S.uColumns,N.columns),v.uniform4f(S.uSourceMap,t.scale[0],t.scale[1],t.offset[0],t.offset[1]),v.uniform1i(S.uScene,Y?0:"flow"===f.scene?1:"orb"===f.scene?2:3),v.uniform3fv(S.uInk,b(f.ink)),v.uniform3fv(S.uHighlight,b("auto"===f.highlight?"#ffffff":f.highlight)),v.uniform1f(S.uHighlightAuto,"auto"===f.highlight?1:0),v.uniform4fv(S.uBackground,r),v.uniform1f(S.uBrightness,f.brightness),v.uniform1f(S.uContrast,f.contrast),v.uniform1f(S.uGamma,f.gamma),v.uniform1f(S.uInvert,f.invert?1:0),v.uniform1f(S.uVignette,f.vignette),v.uniform1f(S.uSourceColor,"source"===f.color?1:0),v.uniform1f(S.uRefraction,e?e.refraction*(be()?.25:1):0),v.uniform1f(S.uGlow,e?e.glow:0),v.uniform1f(S.uTime,re),v.uniform1f(S.uDrift,be()?0:f.drift),v.uniform1f(S.uFluidOn,e&&"fluid"===i&&(_.length||P)?1:0),v.uniform1f(S.uLod,Math.max(0,Math.log2(Math.max(1,n))-.5)),v.uniform1f(S.uSaturation,f.saturation),v.uniform1f(S.uShade,f.shade),v.uniform1f(S.uBackdrop,Y?f.backdrop:0),v.uniform2f(S.uLevels,Y&&"auto"===f.levels?Z[0]:0,Y&&"auto"===f.levels?Z[1]:1),v.bindVertexArray(U),v.drawArrays(v.TRIANGLES,0,3)},Ge=()=>!oe&&!ue&&le&&!("undefined"!=typeof document&&document.hidden),Ne=e=>{if(te=0,!Ge())return;const t=(e-ne)/1e3,n=Math.min(1/30,Math.max(1/480,t));ne=e,he=.9*he+.1*Math.min(t,.25),fe=he>1/24?fe+1:0,fe>45&&de<2&&(fe=0,de++,1===de&&X>1?(f={...f,maxPixelRatio:1},Ve()):de=2);const r=!(ae||de>=2&&!(Y instanceof HTMLVideoElement)||(!(Y instanceof HTMLVideoElement)||Y.paused||Y.ended)&&(be()||!(!Y&&"none"!==f.scene||f.drift>0)));r&&(re+=n*f.speed),Y instanceof HTMLVideoElement&&Y.readyState>=2&&J&&(J=!("requestVideoFrameCallback"in Y),Be(),ie=!0);const i=R(f);let o="fluid"===i&&(null!==ce||e<Te);if(o?((e,t)=>{const n=f.fluid;if(!n||!k.length&&!P)return;const r=I/V,i=ce;if(ce=null,P&&C){const o=5200*n.force*(be()?.35:1);return i&&(Te=t+1e3*Math.min(9,6/Math.max(.3,n.dissipation))),void C.step(i?{from:i.from,to:i.to,force:[(i.to[0]-i.from[0])*o,(i.to[1]-i.from[1])*o]}:null,{dt:Math.min(e,1/60),radius:1.1*n.radius,curl:n.curl,dissipation:n.dissipation,aspect:r})}let o=[0,0];if(i){const e=(be()?1:3)*n.force;o=[(i.to[0]-i.from[0])*r*e*4,(i.to[1]-i.from[1])*e*4],Te=t+7/Math.min(n.dissipation,1.1*n.dissipation)*1e3}v.bindFramebuffer(v.FRAMEBUFFER,k[1-W]),v.viewport(0,0,z,O),v.useProgram(p),v.activeTexture(v.TEXTURE0),v.bindTexture(v.TEXTURE_2D,_[W]),v.uniform1i(D.uPrevious,0),v.uniform2f(D.uSize,z,O),v.uniform1f(D.uAspect,r),v.uniform1f(D.uDt,e),v.uniform1f(D.uDecay,n.dissipation),v.uniform1f(D.uEnergyDecay,1.1*n.dissipation),v.uniform1f(D.uRadius,n.radius),v.uniform1f(D.uSplat,i?1:0),v.uniform2f(D.uFrom,i?.from[0]??0,i?.from[1]??0),v.uniform2f(D.uTo,i?.to[0]??0,i?.to[1]??0),v.uniform2f(D.uForce,o[0],o[1]),v.drawArrays(v.TRIANGLES,0,3),W=1-W})(n,e):ce=null,"lantern"===i&&(o=((e,t)=>{const n=f.lantern;if(me.target){if(!me.at||be())me.at=[...me.target];else{const t=1-Math.exp(-n.follow*e);me.at=[me.at[0]+(me.target[0]-me.at[0])*t,me.at[1]+(me.target[1]-me.at[1])*t]}const r=me.trail[me.trail.length-1];n.trail>0&&(!r||t-r.born>28&&Math.hypot(me.at[0]-r.x,me.at[1]-r.y)>.004)&&(me.trail.push({x:me.at[0],y:me.at[1],born:t}),me.trail.length>12&&me.trail.shift())}return me.strength+=((me.target?1:0)-me.strength)*(1-Math.exp(-e*(me.target?9:5))),me.trail=me.trail.filter(e=>n.trail>0&&t-e.born<4e3*n.trail),ve.fill(0),me.trail.forEach((e,r)=>{ve[3*r]=e.x,ve[3*r+1]=e.y,ve[3*r+2]=.55*Math.exp(-(t-e.born)/1e3/Math.max(.01,n.trail))}),me.strength>.003||me.trail.length>0})(n,e)||o),"trail"===i){const e=!!ge?.active;o=(e=>{const t=Ee();return!(!t||!t.active||(t.step(e),v.bindTexture(v.TEXTURE_2D,pe),v.texSubImage2D(v.TEXTURE_2D,0,0,0,t.width,t.height,v.RGBA,v.UNSIGNED_BYTE,t.pixels),xe=!0,0))})(n)||o,e&&!ge?.active&&(ie=!0)}(r||o||ie)&&(Xe(),ie=!1,!Re||K&&!Y||(Re=!1,Me())),(r||o||ce)&&(te=requestAnimationFrame(Ne))};function He(){!te&&Ge()&&(ne=performance.now(),te=requestAnimationFrame(Ne))}const ze=(e,n)=>{const r=t.getBoundingClientRect();if(!r.width||!r.height)return null;const i=(e-r.left)/r.width,o=(n-r.top)/r.height;return i<-.05||i>1.05||o<-.05||o>1.05?null:[i,1-o]},Oe=e=>{const t=R(f);if("none"===t||ae)return se=null,void(me.target=null);if("lantern"===t)return me.target=e?[e[0],1-e[1]]:null,se=null,void He();if("trail"===t){const t=Ee();if(!t)return;return e?t.move(e[0],1-e[1],f.trail.radius,performance.now()):t.leave(),se=null,void He()}e?(se&&(ce=ce?{from:ce.from,to:e}:{from:se,to:e}),se=e,He()):se=null},We=e=>Oe(ze(e.clientX,e.clientY)),Ye=()=>{se=null,ge?.leave(),me.target&&(me.target=null,He())},qe=h.interactionTarget??window;qe.addEventListener("pointermove",We,{passive:!0}),qe.addEventListener("pointerdown",We,{passive:!0}),document.addEventListener("pointerleave",Ye),qe!==window&&qe.addEventListener("pointerleave",Ye);const $e="function"==typeof ResizeObserver?new ResizeObserver(()=>{Ve()&&He()}):null;$e?.observe(t);const je="function"==typeof IntersectionObserver?new IntersectionObserver(([e])=>{le=e.isIntersecting,le&&(ie=!0,He())},{rootMargin:"120px"}):null;je?.observe(t);const Ke=()=>{document.hidden||(ie=!0,He())};document.addEventListener("visibilitychange",Ke);const Je=()=>{ie=!0,He()};ye?.addEventListener?.("change",Je);const Qe=e=>{e.preventDefault(),ue=!0,te&&cancelAnimationFrame(te),te=0},Ze=()=>{ue=!1,B.length=0,_=[],k=[],C=null,P=!1,pe=null,ge=null,xe=!1;try{Ie(),ie=!0,He()}catch(e){d?.(e instanceof Error?e:new Error(String(e)))}};t.addEventListener("webglcontextlost",Qe),t.addEventListener("webglcontextrestored",Ze);const et=()=>{q&&(q.pause(),q.removeAttribute("src"),q.load(),q=null),Q&&Y instanceof HTMLVideoElement&&"cancelVideoFrameCallback"in Y&&Y.cancelVideoFrameCallback(Q),Q=0},tt=async e=>{const t=++K;if(!e)return et(),Y=null,$=j=0,ie=!0,void He();try{const{element:n,owned:r}=await(e=>"string"!=typeof e?e instanceof HTMLImageElement&&!e.complete?e.decode().then(()=>({element:e,owned:!1})):e instanceof HTMLVideoElement&&e.readyState<2?new Promise((t,n)=>{e.addEventListener("loadeddata",()=>t({element:e,owned:!1}),{once:!0}),e.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0})}):Promise.resolve({element:e,owned:!1}):A.test(e)?new Promise((t,n)=>{const r=document.createElement("video");r.crossOrigin="anonymous",r.muted=!0,r.loop=!0,r.playsInline=!0,r.preload="auto",r.addEventListener("loadeddata",()=>{r.play().catch(()=>{}),t({element:r,owned:!0})},{once:!0}),r.addEventListener("error",()=>n(new Error("The glyph field could not load this video.")),{once:!0}),r.src=e}):new Promise((t,n)=>{const r=new Image;r.crossOrigin="anonymous",r.decoding="async",r.onload=()=>t({element:r,owned:!1}),r.onerror=()=>n(new Error("The glyph field could not load this image.")),r.src=e}))(e);if(oe||t!==K)return void(r&&n instanceof HTMLVideoElement&&(n.pause(),n.removeAttribute("src"),n.load()));et(),q=r&&n instanceof HTMLVideoElement?n:null,Y=n,$=n instanceof HTMLVideoElement?n.videoWidth:n instanceof HTMLImageElement?n.naturalWidth:n.width,j=n instanceof HTMLVideoElement?n.videoHeight:n instanceof HTMLImageElement?n.naturalHeight:n.height,J=!0,n instanceof HTMLVideoElement&&(e=>{const t=e;if(!t.requestVideoFrameCallback)return;const n=()=>{oe||Y!==e||(J=!0,He(),Q=t.requestVideoFrameCallback(n))};Q=t.requestVideoFrameCallback(n)})(n),ue||Be(),ie=!0,He()}catch(e){Re&&t===K&&(Re=!1,Me()),d?.(e instanceof Error?e:new Error(String(e)))}};Ie();const nt=h.source??null;return nt?(K=1,tt(nt)):He(),{canvas:t,ready:we,get settings(){return f},update(e){const n=f;f=E(e,f),e.label&&t.setAttribute("aria-label",e.label),f.maxPixelRatio!==n.maxPixelRatio&&Ve(),f.charset===n.charset&&f.cellSize===n.cellSize&&f.cellAspect===n.cellAspect&&f.font===n.font&&f.fontWeight===n.fontWeight&&f.glyphScale===n.glyphScale||(Ce(),Pe()),f.levels!==n.levels&&Ue();(!!f.fluid!=!!n.fluid||f.fluid&&n.fluid&&f.fluid.resolution!==n.fluid.resolution)&&De(),"source"in e&&tt(e.source??null),ie=!0,He()},setSource:tt,pointer(e,n=0){if(null===e)return void Oe(null);const r=t.getBoundingClientRect();Oe(ze(r.left+e,r.top+n))},redraw(){oe||ue||!I||Xe()},play(){ae=!1,Y instanceof HTMLVideoElement&&Y.play().catch(()=>{}),ie=!0,He()},pause(){ae=!0,Y instanceof HTMLVideoElement&&Y.pause()},resize(){Ve()&&He()},destroy(){oe||(oe=!0,te&&cancelAnimationFrame(te),qe.removeEventListener("pointermove",We),qe.removeEventListener("pointerdown",We),document.removeEventListener("pointerleave",Ye),qe.removeEventListener("pointerleave",Ye),document.removeEventListener("visibilitychange",Ke),ye?.removeEventListener?.("change",Je),t.removeEventListener("webglcontextlost",Qe),t.removeEventListener("webglcontextrestored",Ze),$e?.disconnect(),je?.disconnect(),et(),Y=null,ue||(Se(),v.deleteTexture(w),v.deleteTexture(F),v.deleteProgram(g),v.deleteProgram(p),C?.dispose(),pe&&v.deleteTexture(pe),B.forEach(e=>v.deleteShader(e)),U&&v.deleteVertexArray(U)))}}}export{m as DEFAULT_FIELD_FLUID,f as DEFAULT_FIELD_LANTERN,v as DEFAULT_FIELD_SETTINGS,d as DEFAULT_FIELD_TRAIL,h as FIELD_HOVERS,R as activeFieldHover,y as fieldCellLayout,x as fieldGlyphs,M as fieldLevels,T as fieldSourceRect,L as glyphFieldTier,S as isGlyphFieldSupported,D as mountGlyphField,E as normalizeFieldSettings};//# sourceMappingURL=field.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type AmbientMotion = 'none' | 'caustics' | 'current' | 'reform' | 'sheen' | 'tidal' | 'grain' | 'parallax' | 'weave' | 'print' | 'trace';
|
|
1
|
+
type AmbientMotion = 'none' | 'caustics' | 'current' | 'reform' | 'sheen' | 'tidal' | 'grain' | 'parallax' | 'weave' | 'print' | 'trace' | 'relight' | 'shimmer' | 'breeze' | 'unfold';
|
|
2
2
|
declare const MOTION_STYLES: readonly [{
|
|
3
3
|
readonly value: "none";
|
|
4
4
|
readonly mode: "none";
|
|
@@ -65,6 +65,30 @@ declare const MOTION_STYLES: readonly [{
|
|
|
65
65
|
readonly group: "Light and texture";
|
|
66
66
|
readonly label: "Contour Light";
|
|
67
67
|
readonly description: "Light follows the source image’s tonal contours; every character stays anchored.";
|
|
68
|
+
}, {
|
|
69
|
+
readonly value: "relight";
|
|
70
|
+
readonly mode: "relight";
|
|
71
|
+
readonly group: "Light and texture";
|
|
72
|
+
readonly label: "Relight";
|
|
73
|
+
readonly description: "A slow orbit of light models the image’s midtones, while the character grid stays fixed.";
|
|
74
|
+
}, {
|
|
75
|
+
readonly value: "shimmer";
|
|
76
|
+
readonly mode: "shimmer";
|
|
77
|
+
readonly group: "Light and texture";
|
|
78
|
+
readonly label: "Shimmer";
|
|
79
|
+
readonly description: "Small highlights glint at different moments. Shadows stay quiet and the image stays in place.";
|
|
80
|
+
}, {
|
|
81
|
+
readonly value: "breeze";
|
|
82
|
+
readonly mode: "breeze";
|
|
83
|
+
readonly group: "Flow and depth";
|
|
84
|
+
readonly label: "Breeze";
|
|
85
|
+
readonly description: "A traveling bend gently sways the image, like a print suspended in moving air.";
|
|
86
|
+
}, {
|
|
87
|
+
readonly value: "unfold";
|
|
88
|
+
readonly mode: "unfold";
|
|
89
|
+
readonly group: "Rebuild";
|
|
90
|
+
readonly label: "Unfold";
|
|
91
|
+
readonly description: "Narrow panels turn, soften and return to a complete image, with a readable hold between cycles.";
|
|
68
92
|
}];
|
|
69
93
|
|
|
70
94
|
/** One public catalog for the hero, studio, documentation and integrations. */
|
|
@@ -93,6 +117,11 @@ declare const SURFACE_HOVERS: readonly [{
|
|
|
93
117
|
readonly label: "Emboss";
|
|
94
118
|
readonly group: "Change characters";
|
|
95
119
|
readonly description: "Press a light-and-shadow impression into the characters; the grid stays fixed.";
|
|
120
|
+
}, {
|
|
121
|
+
readonly value: "ripple";
|
|
122
|
+
readonly label: "Ripple rings";
|
|
123
|
+
readonly group: "Change characters";
|
|
124
|
+
readonly description: "Expanding light-and-shadow rings follow your stroke. Faster movement excites stronger rings; the grid stays still.";
|
|
96
125
|
}, {
|
|
97
126
|
readonly value: "water";
|
|
98
127
|
readonly label: "Water";
|
|
@@ -128,6 +157,16 @@ declare const SURFACE_HOVERS: readonly [{
|
|
|
128
157
|
readonly label: "Rake";
|
|
129
158
|
readonly group: "Move the image";
|
|
130
159
|
readonly description: "Comb the image into alternating ribbons that glide back into alignment.";
|
|
160
|
+
}, {
|
|
161
|
+
readonly value: "lens";
|
|
162
|
+
readonly label: "Lens wake";
|
|
163
|
+
readonly group: "Move the image";
|
|
164
|
+
readonly description: "A soft magnifying wake brings details closer, then relaxes back into focus.";
|
|
165
|
+
}, {
|
|
166
|
+
readonly value: "smudge";
|
|
167
|
+
readonly label: "Smudge";
|
|
168
|
+
readonly group: "Move the image";
|
|
169
|
+
readonly description: "Drag the image in the direction of your stroke; a smooth, lingering pull settles without a bounce.";
|
|
131
170
|
}];
|
|
132
171
|
type SurfaceHover = typeof SURFACE_HOVERS[number]['value'];
|
|
133
172
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type AmbientMotion = 'none' | 'caustics' | 'current' | 'reform' | 'sheen' | 'tidal' | 'grain' | 'parallax' | 'weave' | 'print' | 'trace';
|
|
1
|
+
type AmbientMotion = 'none' | 'caustics' | 'current' | 'reform' | 'sheen' | 'tidal' | 'grain' | 'parallax' | 'weave' | 'print' | 'trace' | 'relight' | 'shimmer' | 'breeze' | 'unfold';
|
|
2
2
|
declare const MOTION_STYLES: readonly [{
|
|
3
3
|
readonly value: "none";
|
|
4
4
|
readonly mode: "none";
|
|
@@ -65,6 +65,30 @@ declare const MOTION_STYLES: readonly [{
|
|
|
65
65
|
readonly group: "Light and texture";
|
|
66
66
|
readonly label: "Contour Light";
|
|
67
67
|
readonly description: "Light follows the source image’s tonal contours; every character stays anchored.";
|
|
68
|
+
}, {
|
|
69
|
+
readonly value: "relight";
|
|
70
|
+
readonly mode: "relight";
|
|
71
|
+
readonly group: "Light and texture";
|
|
72
|
+
readonly label: "Relight";
|
|
73
|
+
readonly description: "A slow orbit of light models the image’s midtones, while the character grid stays fixed.";
|
|
74
|
+
}, {
|
|
75
|
+
readonly value: "shimmer";
|
|
76
|
+
readonly mode: "shimmer";
|
|
77
|
+
readonly group: "Light and texture";
|
|
78
|
+
readonly label: "Shimmer";
|
|
79
|
+
readonly description: "Small highlights glint at different moments. Shadows stay quiet and the image stays in place.";
|
|
80
|
+
}, {
|
|
81
|
+
readonly value: "breeze";
|
|
82
|
+
readonly mode: "breeze";
|
|
83
|
+
readonly group: "Flow and depth";
|
|
84
|
+
readonly label: "Breeze";
|
|
85
|
+
readonly description: "A traveling bend gently sways the image, like a print suspended in moving air.";
|
|
86
|
+
}, {
|
|
87
|
+
readonly value: "unfold";
|
|
88
|
+
readonly mode: "unfold";
|
|
89
|
+
readonly group: "Rebuild";
|
|
90
|
+
readonly label: "Unfold";
|
|
91
|
+
readonly description: "Narrow panels turn, soften and return to a complete image, with a readable hold between cycles.";
|
|
68
92
|
}];
|
|
69
93
|
|
|
70
94
|
/** One public catalog for the hero, studio, documentation and integrations. */
|
|
@@ -93,6 +117,11 @@ declare const SURFACE_HOVERS: readonly [{
|
|
|
93
117
|
readonly label: "Emboss";
|
|
94
118
|
readonly group: "Change characters";
|
|
95
119
|
readonly description: "Press a light-and-shadow impression into the characters; the grid stays fixed.";
|
|
120
|
+
}, {
|
|
121
|
+
readonly value: "ripple";
|
|
122
|
+
readonly label: "Ripple rings";
|
|
123
|
+
readonly group: "Change characters";
|
|
124
|
+
readonly description: "Expanding light-and-shadow rings follow your stroke. Faster movement excites stronger rings; the grid stays still.";
|
|
96
125
|
}, {
|
|
97
126
|
readonly value: "water";
|
|
98
127
|
readonly label: "Water";
|
|
@@ -128,6 +157,16 @@ declare const SURFACE_HOVERS: readonly [{
|
|
|
128
157
|
readonly label: "Rake";
|
|
129
158
|
readonly group: "Move the image";
|
|
130
159
|
readonly description: "Comb the image into alternating ribbons that glide back into alignment.";
|
|
160
|
+
}, {
|
|
161
|
+
readonly value: "lens";
|
|
162
|
+
readonly label: "Lens wake";
|
|
163
|
+
readonly group: "Move the image";
|
|
164
|
+
readonly description: "A soft magnifying wake brings details closer, then relaxes back into focus.";
|
|
165
|
+
}, {
|
|
166
|
+
readonly value: "smudge";
|
|
167
|
+
readonly label: "Smudge";
|
|
168
|
+
readonly group: "Move the image";
|
|
169
|
+
readonly description: "Drag the image in the direction of your stroke; a smooth, lingering pull settles without a bounce.";
|
|
131
170
|
}];
|
|
132
171
|
type SurfaceHover = typeof SURFACE_HOVERS[number]['value'];
|
|
133
172
|
|