@pathscale/ui 1.2.10 → 1.2.11
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/LICENSE +22 -22
- package/README.md +59 -59
- package/dist/components/accordion/Accordion.css +158 -158
- package/dist/components/alert/Alert.css +116 -116
- package/dist/components/avatar/Avatar.css +124 -124
- package/dist/components/badge/Badge.css +164 -164
- package/dist/components/breadcrumbs/Breadcrumbs.css +63 -63
- package/dist/components/button/Button.css +194 -194
- package/dist/components/button-group/ButtonGroup.css +117 -117
- package/dist/components/calendar/Calendar.css +258 -258
- package/dist/components/card/Card.css +110 -110
- package/dist/components/chatbubble/ChatBubble.css +164 -164
- package/dist/components/checkbox/Checkbox.css +183 -183
- package/dist/components/checkbox-group/CheckboxGroup.css +10 -10
- package/dist/components/chip/Chip.css +197 -197
- package/dist/components/close-button/CloseButton.css +81 -81
- package/dist/components/color-area/ColorArea.css +54 -54
- package/dist/components/color-field/ColorField.css +88 -88
- package/dist/components/color-picker/ColorPicker.css +28 -28
- package/dist/components/color-slider/ColorSlider.css +57 -57
- package/dist/components/color-swatch/ColorSwatch.css +84 -84
- package/dist/components/color-swatch-picker/ColorSwatchPicker.css +12 -12
- package/dist/components/color-wheel-flower/ColorWheelFlower.css +147 -147
- package/dist/components/combo-box/ComboBox.css +344 -344
- package/dist/components/date-field/DateField.css +215 -215
- package/dist/components/date-picker/DatePicker.css +143 -143
- package/dist/components/date-range-picker/DateRangePicker.css +149 -149
- package/dist/components/description/Description.css +10 -10
- package/dist/components/disclosure/Disclosure.css +84 -84
- package/dist/components/disclosure-group/DisclosureGroup.css +9 -9
- package/dist/components/drawer/Drawer.css +463 -463
- package/dist/components/dropdown/Dropdown.css +211 -211
- package/dist/components/empty-state/EmptyState.css +39 -39
- package/dist/components/error-message/ErrorMessage.css +20 -20
- package/dist/components/field-error/FieldError.css +27 -27
- package/dist/components/fieldset/Fieldset.css +30 -30
- package/dist/components/flex/Flex.css +5 -5
- package/dist/components/floating-dock/FloatingDock.css +208 -208
- package/dist/components/footer/footer.css +51 -51
- package/dist/components/form/Form.css +5 -5
- package/dist/components/glass-panel/GlassPanel.css +359 -359
- package/dist/components/glow-card/GlowCard.css +100 -100
- package/dist/components/grid/Grid.css +5 -5
- package/dist/components/header/Header.css +13 -13
- package/dist/components/icon/Icon.css +10 -10
- package/dist/components/immersive-landing/ImmersiveLanding.css +565 -565
- package/dist/components/input/Input.css +155 -155
- package/dist/components/input-group/InputGroup.css +172 -172
- package/dist/components/input-otp/InputOTP.css +170 -170
- package/dist/components/kbd/Kbd.css +60 -60
- package/dist/components/label/Label.css +30 -30
- package/dist/components/language-switcher/LanguageSwitcher.css +56 -56
- package/dist/components/link/Link.css +131 -131
- package/dist/components/list-box/ListBox.css +148 -148
- package/dist/components/live-chat/LiveChat.css +324 -324
- package/dist/components/menu/Menu.css +199 -199
- package/dist/components/metal-border/MetalBorder.classes.d.ts +26 -0
- package/dist/components/metal-border/MetalBorder.classes.js +27 -0
- package/dist/components/metal-border/MetalBorder.css +40 -0
- package/dist/components/metal-border/MetalBorder.d.ts +20 -0
- package/dist/components/metal-border/MetalBorder.js +322 -0
- package/dist/components/metal-border/engine/color.d.ts +4 -0
- package/dist/components/metal-border/engine/color.js +69 -0
- package/dist/components/metal-border/engine/glow/geometry.d.ts +35 -0
- package/dist/components/metal-border/engine/glow/geometry.js +176 -0
- package/dist/components/metal-border/engine/glow/glow.d.ts +38 -0
- package/dist/components/metal-border/engine/glow/glow.js +246 -0
- package/dist/components/metal-border/engine/index.d.ts +4 -0
- package/dist/components/metal-border/engine/index.js +2 -0
- package/dist/components/metal-border/engine/perfConfig.d.ts +9 -0
- package/dist/components/metal-border/engine/perfConfig.js +10 -0
- package/dist/components/metal-border/engine/presets.d.ts +57 -0
- package/dist/components/metal-border/engine/presets.js +214 -0
- package/dist/components/metal-border/engine/renderer/core.d.ts +69 -0
- package/dist/components/metal-border/engine/renderer/core.js +156 -0
- package/dist/components/metal-border/engine/renderer/loop.d.ts +30 -0
- package/dist/components/metal-border/engine/renderer/loop.js +248 -0
- package/dist/components/metal-border/engine/renderer/sampling.d.ts +14 -0
- package/dist/components/metal-border/engine/renderer/sampling.js +145 -0
- package/dist/components/metal-border/engine/shaders.d.ts +36 -0
- package/dist/components/metal-border/engine/shaders.js +215 -0
- package/dist/components/metal-border/engine/tween.d.ts +17 -0
- package/dist/components/metal-border/engine/tween.js +28 -0
- package/dist/components/metal-border/index.d.ts +2 -0
- package/dist/components/metal-border/index.js +1 -0
- package/dist/components/meter/Meter.css +89 -89
- package/dist/components/modal/Modal.css +320 -320
- package/dist/components/navbar/Navbar.css +110 -110
- package/dist/components/noise-background/NoiseBackground.css +66 -66
- package/dist/components/number-field/NumberField.css +198 -198
- package/dist/components/pagination/Pagination.css +121 -121
- package/dist/components/popover/Popover.css +117 -117
- package/dist/components/progress-bar/ProgressBar.css +104 -104
- package/dist/components/progress-circle/ProgressCircle.css +82 -82
- package/dist/components/radio/Radio.css +129 -129
- package/dist/components/radio-group/RadioGroup.css +58 -58
- package/dist/components/range-calendar/RangeCalendar.css +51 -51
- package/dist/components/scroll-shadow/ScrollShadow.css +93 -93
- package/dist/components/search-field/SearchField.css +183 -183
- package/dist/components/select/Select.css +284 -284
- package/dist/components/separator/Separator.css +35 -35
- package/dist/components/skeleton/Skeleton.css +95 -95
- package/dist/components/slider/Slider.css +122 -122
- package/dist/components/spinner/Spinner.css +131 -131
- package/dist/components/surface/Surface.css +37 -37
- package/dist/components/table/Table.css +426 -426
- package/dist/components/tabs/Tabs.css +203 -203
- package/dist/components/tag/Tag.css +121 -121
- package/dist/components/tag-group/TagGroup.css +22 -22
- package/dist/components/text/Text.css +66 -66
- package/dist/components/text-area/TextArea.css +95 -95
- package/dist/components/text-field/TextField.css +33 -33
- package/dist/components/time-field/TimeField.css +215 -215
- package/dist/components/toast/Toast.css +344 -344
- package/dist/components/toggle/Toggle.css +237 -237
- package/dist/components/toolbar/Toolbar.css +55 -55
- package/dist/components/tooltip/Tooltip.css +153 -153
- package/dist/index.css +74 -74
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/purge-manifest.json +15100 -14847
- package/dist/styles/base/index.css +43 -43
- package/dist/styles/themes/dark.css +141 -141
- package/dist/styles/themes/light.css +143 -143
- package/package.json +2 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { CANONICAL_GL_SIZE, GL_DPR_CAP } from "../perfConfig.js";
|
|
2
|
+
import { PRESETS } from "../presets.js";
|
|
3
|
+
import { FRAG_SHADER_SRC, VERT_SHADER_SRC, compileShader, linkProgram } from "../shaders.js";
|
|
4
|
+
const CANONICAL_PILL_W = 140;
|
|
5
|
+
const CANONICAL_PILL_H = 40;
|
|
6
|
+
const PILL_SHADER_SCALE = 1.6;
|
|
7
|
+
const CIRCLE_SHADER_SCALE = 1.3;
|
|
8
|
+
let SHARED = null;
|
|
9
|
+
let _onContextRestored = null;
|
|
10
|
+
function setContextRestoredCallback(cb) {
|
|
11
|
+
_onContextRestored = cb;
|
|
12
|
+
}
|
|
13
|
+
const UNIFORM_NAMES = [
|
|
14
|
+
"u_resolution",
|
|
15
|
+
"u_time",
|
|
16
|
+
"u_color1",
|
|
17
|
+
"u_color2",
|
|
18
|
+
"u_color3",
|
|
19
|
+
"u_color4",
|
|
20
|
+
"u_color5",
|
|
21
|
+
"u_color6",
|
|
22
|
+
"u_color7",
|
|
23
|
+
"u_alpha1",
|
|
24
|
+
"u_alpha2",
|
|
25
|
+
"u_alpha3",
|
|
26
|
+
"u_alpha4",
|
|
27
|
+
"u_alpha5",
|
|
28
|
+
"u_alpha6",
|
|
29
|
+
"u_alpha7",
|
|
30
|
+
"u_intensity",
|
|
31
|
+
"u_scale",
|
|
32
|
+
"u_direction",
|
|
33
|
+
"u_softness",
|
|
34
|
+
"u_distortion",
|
|
35
|
+
"u_complexity",
|
|
36
|
+
"u_shape",
|
|
37
|
+
"u_vignette",
|
|
38
|
+
"u_vigOpacity",
|
|
39
|
+
"u_blur",
|
|
40
|
+
"u_shaderOpacity"
|
|
41
|
+
];
|
|
42
|
+
function buildGLPipeline(gl) {
|
|
43
|
+
gl.enable(gl.BLEND);
|
|
44
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
45
|
+
const vert = compileShader(gl, gl.VERTEX_SHADER, VERT_SHADER_SRC);
|
|
46
|
+
const frag = compileShader(gl, gl.FRAGMENT_SHADER, FRAG_SHADER_SRC);
|
|
47
|
+
const program = linkProgram(gl, vert, frag);
|
|
48
|
+
gl.useProgram(program);
|
|
49
|
+
const buffer = gl.createBuffer();
|
|
50
|
+
if (!buffer) throw new Error("metal-fx: gl.createBuffer returned null");
|
|
51
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
52
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
|
53
|
+
-1,
|
|
54
|
+
-1,
|
|
55
|
+
1,
|
|
56
|
+
-1,
|
|
57
|
+
-1,
|
|
58
|
+
1,
|
|
59
|
+
-1,
|
|
60
|
+
1,
|
|
61
|
+
1,
|
|
62
|
+
-1,
|
|
63
|
+
1,
|
|
64
|
+
1
|
|
65
|
+
]), gl.STATIC_DRAW);
|
|
66
|
+
const posLoc = gl.getAttribLocation(program, "a_position");
|
|
67
|
+
gl.enableVertexAttribArray(posLoc);
|
|
68
|
+
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
69
|
+
const uniforms = {};
|
|
70
|
+
for (const n of UNIFORM_NAMES)uniforms[n] = gl.getUniformLocation(program, n);
|
|
71
|
+
return {
|
|
72
|
+
program,
|
|
73
|
+
buffer,
|
|
74
|
+
uniforms
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function ensureSharedRenderer() {
|
|
78
|
+
if (SHARED) return SHARED;
|
|
79
|
+
const dpr = Math.min(GL_DPR_CAP, "u" > typeof window ? window.devicePixelRatio || 1 : 1);
|
|
80
|
+
const size = Math.round(CANONICAL_GL_SIZE * dpr);
|
|
81
|
+
const useOffscreen = "u" > typeof OffscreenCanvas;
|
|
82
|
+
let glCanvas;
|
|
83
|
+
let gl;
|
|
84
|
+
if (useOffscreen) {
|
|
85
|
+
glCanvas = new OffscreenCanvas(size, size);
|
|
86
|
+
gl = glCanvas.getContext("webgl", {
|
|
87
|
+
alpha: true,
|
|
88
|
+
premultipliedAlpha: false,
|
|
89
|
+
antialias: false
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
const htmlCanvas = document.createElement("canvas");
|
|
93
|
+
htmlCanvas.width = size;
|
|
94
|
+
htmlCanvas.height = size;
|
|
95
|
+
gl = htmlCanvas.getContext("webgl", {
|
|
96
|
+
alpha: true,
|
|
97
|
+
premultipliedAlpha: false,
|
|
98
|
+
antialias: false,
|
|
99
|
+
preserveDrawingBuffer: true
|
|
100
|
+
}) ?? htmlCanvas.getContext("experimental-webgl");
|
|
101
|
+
glCanvas = htmlCanvas;
|
|
102
|
+
}
|
|
103
|
+
if (!gl) throw new Error("metal-fx: WebGL not supported");
|
|
104
|
+
const { program, buffer, uniforms } = buildGLPipeline(gl);
|
|
105
|
+
const onContextLost = (e)=>{
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
if (SHARED) SHARED.contextLost = true;
|
|
108
|
+
};
|
|
109
|
+
const onContextRestored = ()=>{
|
|
110
|
+
if (!SHARED) return;
|
|
111
|
+
const rebuilt = buildGLPipeline(SHARED.gl);
|
|
112
|
+
SHARED.program = rebuilt.program;
|
|
113
|
+
SHARED.buffer = rebuilt.buffer;
|
|
114
|
+
SHARED.uniforms = rebuilt.uniforms;
|
|
115
|
+
SHARED.presetDirty = true;
|
|
116
|
+
SHARED.contextLost = false;
|
|
117
|
+
_onContextRestored?.();
|
|
118
|
+
};
|
|
119
|
+
glCanvas.addEventListener("webglcontextlost", onContextLost, false);
|
|
120
|
+
glCanvas.addEventListener("webglcontextrestored", onContextRestored, false);
|
|
121
|
+
SHARED = {
|
|
122
|
+
glCanvas,
|
|
123
|
+
gl,
|
|
124
|
+
program,
|
|
125
|
+
buffer,
|
|
126
|
+
uniforms,
|
|
127
|
+
preset: PRESETS.chromatic.modes.dark,
|
|
128
|
+
presetName: "chromatic",
|
|
129
|
+
presetTheme: "dark",
|
|
130
|
+
presetDirty: true,
|
|
131
|
+
contextLost: false,
|
|
132
|
+
useOffscreen,
|
|
133
|
+
startMs: performance.now(),
|
|
134
|
+
pausedMs: 0,
|
|
135
|
+
pausedAtMs: null,
|
|
136
|
+
rafId: 0,
|
|
137
|
+
dpr,
|
|
138
|
+
instances: new Set(),
|
|
139
|
+
frameCount: 0,
|
|
140
|
+
glowPixels: new Uint8Array(size * size * 4),
|
|
141
|
+
glowPixelsW: size,
|
|
142
|
+
glowPixelsH: size
|
|
143
|
+
};
|
|
144
|
+
return SHARED;
|
|
145
|
+
}
|
|
146
|
+
function teardownSharedRenderer() {
|
|
147
|
+
if (!SHARED) return;
|
|
148
|
+
const { gl, program, buffer } = SHARED;
|
|
149
|
+
try {
|
|
150
|
+
gl.deleteBuffer(buffer);
|
|
151
|
+
gl.deleteProgram(program);
|
|
152
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
153
|
+
} catch {}
|
|
154
|
+
SHARED = null;
|
|
155
|
+
}
|
|
156
|
+
export { CANONICAL_PILL_H, CANONICAL_PILL_W, CIRCLE_SHADER_SCALE, PILL_SHADER_SCALE, SHARED, ensureSharedRenderer, setContextRestoredCallback, teardownSharedRenderer };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type PresetName, type PresetTheme } from "../presets";
|
|
2
|
+
import { type MetalFxInstance } from "./core";
|
|
3
|
+
interface CreateInstanceOptions {
|
|
4
|
+
hostCanvas: HTMLCanvasElement;
|
|
5
|
+
cssWidth: number;
|
|
6
|
+
cssHeight: number;
|
|
7
|
+
cornerRadius: number;
|
|
8
|
+
kind: "pill" | "circle";
|
|
9
|
+
presetName: PresetName;
|
|
10
|
+
presetTheme: PresetTheme;
|
|
11
|
+
shaderScale?: number;
|
|
12
|
+
ringCssPx?: number;
|
|
13
|
+
opacityMul?: number;
|
|
14
|
+
paused?: boolean;
|
|
15
|
+
scale?: number;
|
|
16
|
+
onAfterFrame?: () => void;
|
|
17
|
+
onFirstCopy?: () => void;
|
|
18
|
+
}
|
|
19
|
+
export declare function createInstance(opts: CreateInstanceOptions): MetalFxInstance;
|
|
20
|
+
export declare function destroyInstance(inst: MetalFxInstance): void;
|
|
21
|
+
export declare function registerGlowInstance(inst: MetalFxInstance): void;
|
|
22
|
+
export declare function unregisterGlowInstance(inst: MetalFxInstance): void;
|
|
23
|
+
export declare function updateInstance(inst: MetalFxInstance, patch: Partial<Pick<MetalFxInstance, "cssWidth" | "cssHeight" | "cornerRadius" | "kind" | "shaderScale" | "ringCssPx" | "opacityMul" | "paused" | "presetName" | "presetTheme" | "scale">>): void;
|
|
24
|
+
export declare function setInstanceVisible(inst: MetalFxInstance, visible: boolean): void;
|
|
25
|
+
export declare function pauseShared(): void;
|
|
26
|
+
export declare function resumeShared(): void;
|
|
27
|
+
export declare function getSharedFrameCount(): number;
|
|
28
|
+
export type GlowCallback = (inst: MetalFxInstance, nowMs: number) => void;
|
|
29
|
+
export declare function setGlowCallback(cb: GlowCallback | null): void;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { hexToRgb } from "../color.js";
|
|
2
|
+
import { FRAME_INTERVAL_MS, GLOW_SKIP_FRAMES } from "../perfConfig.js";
|
|
3
|
+
import { PRESETS } from "../presets.js";
|
|
4
|
+
import { CANONICAL_PILL_H, CANONICAL_PILL_W, CIRCLE_SHADER_SCALE, PILL_SHADER_SCALE, SHARED, ensureSharedRenderer, setContextRestoredCallback, teardownSharedRenderer } from "./core.js";
|
|
5
|
+
import { refreshGlowPixels } from "./sampling.js";
|
|
6
|
+
setContextRestoredCallback(()=>{
|
|
7
|
+
if (SHARED && SHARED.instances.size > 0 && null === SHARED.pausedAtMs) startSharedLoop();
|
|
8
|
+
});
|
|
9
|
+
if ("u" > typeof document) document.addEventListener("visibilitychange", ()=>{
|
|
10
|
+
if (!SHARED || null !== SHARED.pausedAtMs || SHARED.contextLost) return;
|
|
11
|
+
if (document.hidden) stopSharedLoop();
|
|
12
|
+
else if (SHARED.instances.size > 0) startSharedLoop();
|
|
13
|
+
});
|
|
14
|
+
function createInstance(opts) {
|
|
15
|
+
const renderer = ensureSharedRenderer();
|
|
16
|
+
const ctx = opts.hostCanvas.getContext("2d", {
|
|
17
|
+
alpha: true
|
|
18
|
+
});
|
|
19
|
+
if (!ctx) throw new Error("metal-fx: canvas 2D context unavailable");
|
|
20
|
+
const scale = opts.scale ?? 1;
|
|
21
|
+
const inst = {
|
|
22
|
+
canvas: opts.hostCanvas,
|
|
23
|
+
ctx,
|
|
24
|
+
cssWidth: opts.cssWidth,
|
|
25
|
+
cssHeight: opts.cssHeight,
|
|
26
|
+
cornerRadius: opts.cornerRadius,
|
|
27
|
+
kind: opts.kind,
|
|
28
|
+
ringCssPx: opts.ringCssPx ?? ("circle" === opts.kind ? 2 : 1) * scale,
|
|
29
|
+
shaderScale: opts.shaderScale ?? ("circle" === opts.kind ? CIRCLE_SHADER_SCALE : PILL_SHADER_SCALE) * scale,
|
|
30
|
+
opacityMul: opts.opacityMul ?? 1,
|
|
31
|
+
visible: true,
|
|
32
|
+
paused: opts.paused ?? false,
|
|
33
|
+
everCopied: false,
|
|
34
|
+
dpr: "u" > typeof window ? window.devicePixelRatio || 1 : 1,
|
|
35
|
+
presetName: opts.presetName,
|
|
36
|
+
presetTheme: opts.presetTheme,
|
|
37
|
+
glowEnabled: false,
|
|
38
|
+
scale,
|
|
39
|
+
onAfterFrame: opts.onAfterFrame,
|
|
40
|
+
onFirstCopy: opts.onFirstCopy
|
|
41
|
+
};
|
|
42
|
+
resizeInstanceCanvas(inst);
|
|
43
|
+
renderer.instances.add(inst);
|
|
44
|
+
if (0 === renderer.rafId && null === renderer.pausedAtMs) startSharedLoop();
|
|
45
|
+
return inst;
|
|
46
|
+
}
|
|
47
|
+
function destroyInstance(inst) {
|
|
48
|
+
if (!SHARED) return;
|
|
49
|
+
SHARED.instances["delete"](inst);
|
|
50
|
+
if (0 === SHARED.instances.size) {
|
|
51
|
+
stopSharedLoop();
|
|
52
|
+
teardownSharedRenderer();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function registerGlowInstance(inst) {
|
|
56
|
+
inst.glowEnabled = true;
|
|
57
|
+
}
|
|
58
|
+
function unregisterGlowInstance(inst) {
|
|
59
|
+
inst.glowEnabled = false;
|
|
60
|
+
}
|
|
61
|
+
function updateInstance(inst, patch) {
|
|
62
|
+
let dirty = false;
|
|
63
|
+
if (void 0 !== patch.cssWidth && patch.cssWidth !== inst.cssWidth) {
|
|
64
|
+
inst.cssWidth = patch.cssWidth;
|
|
65
|
+
dirty = true;
|
|
66
|
+
}
|
|
67
|
+
if (void 0 !== patch.cssHeight && patch.cssHeight !== inst.cssHeight) {
|
|
68
|
+
inst.cssHeight = patch.cssHeight;
|
|
69
|
+
dirty = true;
|
|
70
|
+
}
|
|
71
|
+
if (void 0 !== patch.cornerRadius) inst.cornerRadius = patch.cornerRadius;
|
|
72
|
+
if (void 0 !== patch.scale) inst.scale = patch.scale;
|
|
73
|
+
if (void 0 !== patch.kind && patch.kind !== inst.kind) {
|
|
74
|
+
inst.kind = patch.kind;
|
|
75
|
+
if (void 0 === patch.shaderScale) inst.shaderScale = ("circle" === patch.kind ? CIRCLE_SHADER_SCALE : PILL_SHADER_SCALE) * inst.scale;
|
|
76
|
+
if (void 0 === patch.ringCssPx) inst.ringCssPx = ("circle" === patch.kind ? 2 : 1) * inst.scale;
|
|
77
|
+
}
|
|
78
|
+
if (void 0 !== patch.shaderScale) inst.shaderScale = patch.shaderScale;
|
|
79
|
+
if (void 0 !== patch.ringCssPx) inst.ringCssPx = patch.ringCssPx;
|
|
80
|
+
if (void 0 !== patch.opacityMul) inst.opacityMul = patch.opacityMul;
|
|
81
|
+
if (void 0 !== patch.presetName) inst.presetName = patch.presetName;
|
|
82
|
+
if (void 0 !== patch.presetTheme) inst.presetTheme = patch.presetTheme;
|
|
83
|
+
if (void 0 !== patch.paused && patch.paused !== inst.paused) {
|
|
84
|
+
inst.paused = patch.paused;
|
|
85
|
+
if (!patch.paused && SHARED && 0 === SHARED.rafId && null === SHARED.pausedAtMs && !SHARED.contextLost) startSharedLoop();
|
|
86
|
+
}
|
|
87
|
+
if (dirty) resizeInstanceCanvas(inst);
|
|
88
|
+
}
|
|
89
|
+
function setInstanceVisible(inst, visible) {
|
|
90
|
+
inst.visible = visible;
|
|
91
|
+
if (visible && SHARED && 0 === SHARED.rafId && null === SHARED.pausedAtMs && !SHARED.contextLost) startSharedLoop();
|
|
92
|
+
}
|
|
93
|
+
function syncRendererPreset(name, theme) {
|
|
94
|
+
const shared = ensureSharedRenderer();
|
|
95
|
+
if (shared.presetName === name && shared.presetTheme === theme && !shared.presetDirty) return;
|
|
96
|
+
shared.preset = PRESETS[name].modes[theme];
|
|
97
|
+
shared.presetName = name;
|
|
98
|
+
shared.presetTheme = theme;
|
|
99
|
+
shared.presetDirty = true;
|
|
100
|
+
}
|
|
101
|
+
function pauseShared() {
|
|
102
|
+
if (!SHARED || null !== SHARED.pausedAtMs) return;
|
|
103
|
+
SHARED.pausedAtMs = performance.now();
|
|
104
|
+
stopSharedLoop();
|
|
105
|
+
}
|
|
106
|
+
function resumeShared() {
|
|
107
|
+
if (!SHARED || null === SHARED.pausedAtMs) return;
|
|
108
|
+
SHARED.pausedMs += performance.now() - SHARED.pausedAtMs;
|
|
109
|
+
SHARED.pausedAtMs = null;
|
|
110
|
+
if (SHARED.instances.size > 0) startSharedLoop();
|
|
111
|
+
}
|
|
112
|
+
function getSharedFrameCount() {
|
|
113
|
+
return SHARED?.frameCount ?? 0;
|
|
114
|
+
}
|
|
115
|
+
let _glowCallback = null;
|
|
116
|
+
function setGlowCallback(cb) {
|
|
117
|
+
_glowCallback = cb;
|
|
118
|
+
}
|
|
119
|
+
function resizeInstanceCanvas(inst) {
|
|
120
|
+
inst.dpr = "u" > typeof window ? window.devicePixelRatio || 1 : 1;
|
|
121
|
+
const w = Math.max(1, Math.round(inst.cssWidth * inst.dpr));
|
|
122
|
+
const h = Math.max(1, Math.round(inst.cssHeight * inst.dpr));
|
|
123
|
+
if (inst.canvas.width !== w) inst.canvas.width = w;
|
|
124
|
+
if (inst.canvas.height !== h) inst.canvas.height = h;
|
|
125
|
+
}
|
|
126
|
+
function punchInnerHole(inst) {
|
|
127
|
+
const { ctx, dpr, canvas } = inst;
|
|
128
|
+
const stroke = inst.ringCssPx * dpr;
|
|
129
|
+
const w = canvas.width;
|
|
130
|
+
const h = canvas.height;
|
|
131
|
+
const innerR = Math.max(0, (inst.cornerRadius - inst.ringCssPx) * dpr);
|
|
132
|
+
ctx.save();
|
|
133
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
134
|
+
ctx.fillStyle = "#000";
|
|
135
|
+
ctx.beginPath();
|
|
136
|
+
ctx.roundRect(stroke, stroke, w - 2 * stroke, h - 2 * stroke, innerR);
|
|
137
|
+
ctx.fill();
|
|
138
|
+
ctx.restore();
|
|
139
|
+
}
|
|
140
|
+
function copyShaderToInstance(inst) {
|
|
141
|
+
if (!SHARED) return;
|
|
142
|
+
const src = SHARED.glCanvas;
|
|
143
|
+
const dpr = inst.dpr;
|
|
144
|
+
const dw = inst.canvas.width;
|
|
145
|
+
const dh = inst.canvas.height;
|
|
146
|
+
if (dw < 1 || dh < 1) return;
|
|
147
|
+
const cw = SHARED.glCanvas.width;
|
|
148
|
+
const ch = SHARED.glCanvas.height;
|
|
149
|
+
const bdW = CANONICAL_PILL_W * dpr;
|
|
150
|
+
const bdH = CANONICAL_PILL_H * dpr;
|
|
151
|
+
let srcW = cw / bdW * dw / inst.shaderScale;
|
|
152
|
+
let srcH = ch / bdH * dh / inst.shaderScale;
|
|
153
|
+
if (srcW > cw) srcW = cw;
|
|
154
|
+
if (srcH > ch) srcH = ch;
|
|
155
|
+
const sx = Math.max(0, (cw - srcW) / 2);
|
|
156
|
+
const sy = Math.max(0, (ch - srcH) / 2);
|
|
157
|
+
inst.ctx.clearRect(0, 0, dw, dh);
|
|
158
|
+
if (inst.opacityMul < 1) inst.ctx.globalAlpha = inst.opacityMul;
|
|
159
|
+
inst.ctx.drawImage(src, sx, sy, srcW, srcH, 0, 0, dw, dh);
|
|
160
|
+
if (inst.opacityMul < 1) inst.ctx.globalAlpha = 1;
|
|
161
|
+
punchInnerHole(inst);
|
|
162
|
+
if (inst.onFirstCopy) {
|
|
163
|
+
const cb = inst.onFirstCopy;
|
|
164
|
+
inst.onFirstCopy = void 0;
|
|
165
|
+
cb();
|
|
166
|
+
}
|
|
167
|
+
inst.onAfterFrame?.();
|
|
168
|
+
}
|
|
169
|
+
function uploadPresetUniforms() {
|
|
170
|
+
if (!SHARED) return;
|
|
171
|
+
const { gl, uniforms, preset, glCanvas } = SHARED;
|
|
172
|
+
if (uniforms.u_resolution) gl.uniform2f(uniforms.u_resolution, glCanvas.width, glCanvas.height);
|
|
173
|
+
for(let i = 0; i < 7; i++){
|
|
174
|
+
const cLoc = uniforms[`u_color${i + 1}`];
|
|
175
|
+
if (cLoc) {
|
|
176
|
+
const [r, g, b] = hexToRgb(preset.colors[i]);
|
|
177
|
+
gl.uniform3f(cLoc, r, g, b);
|
|
178
|
+
}
|
|
179
|
+
const aLoc = uniforms[`u_alpha${i + 1}`];
|
|
180
|
+
if (aLoc) gl.uniform1f(aLoc, preset.alphas[i]);
|
|
181
|
+
}
|
|
182
|
+
if (uniforms.u_intensity) gl.uniform1f(uniforms.u_intensity, preset.intensity);
|
|
183
|
+
if (uniforms.u_scale) gl.uniform1f(uniforms.u_scale, preset.scale);
|
|
184
|
+
if (uniforms.u_direction) gl.uniform1f(uniforms.u_direction, preset.direction * Math.PI / 180);
|
|
185
|
+
if (uniforms.u_softness) gl.uniform1f(uniforms.u_softness, preset.softness);
|
|
186
|
+
if (uniforms.u_distortion) gl.uniform1f(uniforms.u_distortion, preset.distortion);
|
|
187
|
+
if (uniforms.u_complexity) gl.uniform1f(uniforms.u_complexity, preset.complexity);
|
|
188
|
+
if (uniforms.u_shape) gl.uniform1f(uniforms.u_shape, preset.shape);
|
|
189
|
+
if (uniforms.u_vignette) gl.uniform1f(uniforms.u_vignette, preset.vignette);
|
|
190
|
+
if (uniforms.u_vigOpacity) gl.uniform1f(uniforms.u_vigOpacity, preset.vigOpacity);
|
|
191
|
+
if (uniforms.u_blur) gl.uniform1f(uniforms.u_blur, preset.blur);
|
|
192
|
+
if (uniforms.u_shaderOpacity) gl.uniform1f(uniforms.u_shaderOpacity, preset.shaderOpacity);
|
|
193
|
+
SHARED.presetDirty = false;
|
|
194
|
+
}
|
|
195
|
+
function renderSharedFrame(now, presetName, presetTheme) {
|
|
196
|
+
if (!SHARED) return;
|
|
197
|
+
syncRendererPreset(presetName, presetTheme);
|
|
198
|
+
const { gl, uniforms, preset, glCanvas } = SHARED;
|
|
199
|
+
const t = (now - SHARED.startMs - SHARED.pausedMs) / 1000 * preset.speed;
|
|
200
|
+
gl.viewport(0, 0, glCanvas.width, glCanvas.height);
|
|
201
|
+
gl.clearColor(0, 0, 0, 0);
|
|
202
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
203
|
+
if (SHARED.presetDirty) uploadPresetUniforms();
|
|
204
|
+
if (uniforms.u_time) gl.uniform1f(uniforms.u_time, t);
|
|
205
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
206
|
+
SHARED.frameCount++;
|
|
207
|
+
}
|
|
208
|
+
let lastFrameMs = 0;
|
|
209
|
+
function tick(now) {
|
|
210
|
+
if (!SHARED) return;
|
|
211
|
+
if (SHARED.contextLost) {
|
|
212
|
+
SHARED.rafId = 0;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
let anyWork = false;
|
|
216
|
+
for (const inst of SHARED.instances)if (inst.visible && (!inst.paused || !inst.everCopied)) {
|
|
217
|
+
anyWork = true;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
if (!anyWork) {
|
|
221
|
+
SHARED.rafId = 0;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
SHARED.rafId = requestAnimationFrame(tick);
|
|
225
|
+
if (now - lastFrameMs < FRAME_INTERVAL_MS) return;
|
|
226
|
+
lastFrameMs = now;
|
|
227
|
+
for (const inst of SHARED.instances)if (inst.visible) {
|
|
228
|
+
if (!inst.paused || !inst.everCopied) {
|
|
229
|
+
renderSharedFrame(now, inst.presetName, inst.presetTheme);
|
|
230
|
+
copyShaderToInstance(inst);
|
|
231
|
+
inst.everCopied = true;
|
|
232
|
+
if (_glowCallback && inst.glowEnabled && !inst.paused && SHARED.frameCount % GLOW_SKIP_FRAMES === 0) {
|
|
233
|
+
refreshGlowPixels();
|
|
234
|
+
_glowCallback(inst, now);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function startSharedLoop() {
|
|
240
|
+
if (!SHARED || 0 !== SHARED.rafId) return;
|
|
241
|
+
SHARED.rafId = requestAnimationFrame(tick);
|
|
242
|
+
}
|
|
243
|
+
function stopSharedLoop() {
|
|
244
|
+
if (!SHARED) return;
|
|
245
|
+
if (0 !== SHARED.rafId) cancelAnimationFrame(SHARED.rafId);
|
|
246
|
+
SHARED.rafId = 0;
|
|
247
|
+
}
|
|
248
|
+
export { createInstance, destroyInstance, getSharedFrameCount, pauseShared, registerGlowInstance, resumeShared, setGlowCallback, setInstanceVisible, unregisterGlowInstance, updateInstance };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pixel readback and luminance/colour sampling from the shared GL canvas.
|
|
3
|
+
*
|
|
4
|
+
* All glow luminance/color sampling reads from a shared pixel buffer
|
|
5
|
+
* (SHARED.glowPixels). The buffer is refreshed via gl.readPixels at most
|
|
6
|
+
* every GLOW_READBACK_INTERVAL_MS to avoid the expensive GPU→CPU pipeline
|
|
7
|
+
* flush on every frame. The plasma shader evolves slowly so 200ms-stale
|
|
8
|
+
* data is visually indistinguishable.
|
|
9
|
+
*/
|
|
10
|
+
import { type MetalFxInstance, type ShaderRGB } from "./core";
|
|
11
|
+
export declare function refreshGlowPixels(): void;
|
|
12
|
+
export declare function sampleShaderLumAt(inst: MetalFxInstance, cssPxX: number, cssPxY: number, radius: number): number;
|
|
13
|
+
export declare function sampleShaderRGBAt(inst: MetalFxInstance, cssPxX: number, cssPxY: number, radius: number): ShaderRGB;
|
|
14
|
+
export declare function sampleShaderRGBChromatic(inst: MetalFxInstance, cssPxX: number, cssPxY: number, radius: number): ShaderRGB;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { CANONICAL_PILL_H, CANONICAL_PILL_W, SHARED } from "./core.js";
|
|
2
|
+
function refreshGlowPixels() {
|
|
3
|
+
if (!SHARED) return;
|
|
4
|
+
const { gl, glCanvas } = SHARED;
|
|
5
|
+
const cw = glCanvas.width;
|
|
6
|
+
const ch = glCanvas.height;
|
|
7
|
+
if (SHARED.glowPixelsW !== cw || SHARED.glowPixelsH !== ch) {
|
|
8
|
+
SHARED.glowPixelsW = cw;
|
|
9
|
+
SHARED.glowPixelsH = ch;
|
|
10
|
+
SHARED.glowPixels = new Uint8Array(cw * ch * 4);
|
|
11
|
+
}
|
|
12
|
+
gl.readPixels(0, 0, cw, ch, gl.RGBA, gl.UNSIGNED_BYTE, SHARED.glowPixels);
|
|
13
|
+
}
|
|
14
|
+
const _map = {
|
|
15
|
+
bx: 0,
|
|
16
|
+
by: 0
|
|
17
|
+
};
|
|
18
|
+
function mapToGlowBuf(inst, cssPxX, cssPxY) {
|
|
19
|
+
if (!SHARED) {
|
|
20
|
+
_map.bx = 0;
|
|
21
|
+
_map.by = 0;
|
|
22
|
+
return _map;
|
|
23
|
+
}
|
|
24
|
+
const { glCanvas } = SHARED;
|
|
25
|
+
const cw = glCanvas.width;
|
|
26
|
+
const ch = glCanvas.height;
|
|
27
|
+
const dpr = inst.dpr;
|
|
28
|
+
const dw = inst.cssWidth * dpr;
|
|
29
|
+
const dh = inst.cssHeight * dpr;
|
|
30
|
+
const bdW = CANONICAL_PILL_W * dpr;
|
|
31
|
+
const bdH = CANONICAL_PILL_H * dpr;
|
|
32
|
+
let srcW = cw / bdW * dw / inst.shaderScale;
|
|
33
|
+
let srcH = ch / bdH * dh / inst.shaderScale;
|
|
34
|
+
if (srcW > cw) srcW = cw;
|
|
35
|
+
if (srcH > ch) srcH = ch;
|
|
36
|
+
const sx = (cw - srcW) / 2;
|
|
37
|
+
const sy = (ch - srcH) / 2;
|
|
38
|
+
const glX = sx + cssPxX / inst.cssWidth * srcW;
|
|
39
|
+
const glY = sy + cssPxY / inst.cssHeight * srcH;
|
|
40
|
+
_map.bx = Math.round(glX);
|
|
41
|
+
_map.by = Math.round(ch - 1 - glY);
|
|
42
|
+
return _map;
|
|
43
|
+
}
|
|
44
|
+
const _sr = {
|
|
45
|
+
r: 0,
|
|
46
|
+
g: 0,
|
|
47
|
+
b: 0,
|
|
48
|
+
lum: 0,
|
|
49
|
+
count: 0
|
|
50
|
+
};
|
|
51
|
+
function sampleRegion(buf, W, H, bx, by, radius) {
|
|
52
|
+
const r = Math.max(1, 0 | radius);
|
|
53
|
+
const x0 = Math.max(0, bx - r);
|
|
54
|
+
const x1 = Math.min(W, bx + r + 1);
|
|
55
|
+
const y0 = Math.max(0, by - r);
|
|
56
|
+
const y1 = Math.min(H, by + r + 1);
|
|
57
|
+
_sr.r = 0;
|
|
58
|
+
_sr.g = 0;
|
|
59
|
+
_sr.b = 0;
|
|
60
|
+
_sr.lum = 0;
|
|
61
|
+
_sr.count = 0;
|
|
62
|
+
for(let py = y0; py < y1; py++){
|
|
63
|
+
const row = py * W;
|
|
64
|
+
for(let px = x0; px < x1; px++){
|
|
65
|
+
const i = (row + px) * 4;
|
|
66
|
+
_sr.r += buf[i];
|
|
67
|
+
_sr.g += buf[i + 1];
|
|
68
|
+
_sr.b += buf[i + 2];
|
|
69
|
+
_sr.lum += (0.2126 * buf[i] + 0.7152 * buf[i + 1] + 0.0722 * buf[i + 2]) / 255;
|
|
70
|
+
_sr.count++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return _sr;
|
|
74
|
+
}
|
|
75
|
+
const _rgb = {
|
|
76
|
+
r: 255,
|
|
77
|
+
g: 255,
|
|
78
|
+
b: 255
|
|
79
|
+
};
|
|
80
|
+
function sampleShaderLumAt(inst, cssPxX, cssPxY, radius) {
|
|
81
|
+
if (!SHARED) return 0;
|
|
82
|
+
const m = mapToGlowBuf(inst, cssPxX, cssPxY);
|
|
83
|
+
const s = sampleRegion(SHARED.glowPixels, SHARED.glowPixelsW, SHARED.glowPixelsH, m.bx, m.by, radius);
|
|
84
|
+
return s.count > 0 ? s.lum / s.count : 0;
|
|
85
|
+
}
|
|
86
|
+
function sampleShaderRGBAt(inst, cssPxX, cssPxY, radius) {
|
|
87
|
+
if (!SHARED) {
|
|
88
|
+
_rgb.r = 255;
|
|
89
|
+
_rgb.g = 255;
|
|
90
|
+
_rgb.b = 255;
|
|
91
|
+
return _rgb;
|
|
92
|
+
}
|
|
93
|
+
const m = mapToGlowBuf(inst, cssPxX, cssPxY);
|
|
94
|
+
const s = sampleRegion(SHARED.glowPixels, SHARED.glowPixelsW, SHARED.glowPixelsH, m.bx, m.by, radius);
|
|
95
|
+
if (0 === s.count) {
|
|
96
|
+
_rgb.r = 255;
|
|
97
|
+
_rgb.g = 255;
|
|
98
|
+
_rgb.b = 255;
|
|
99
|
+
return _rgb;
|
|
100
|
+
}
|
|
101
|
+
_rgb.r = s.r / s.count;
|
|
102
|
+
_rgb.g = s.g / s.count;
|
|
103
|
+
_rgb.b = s.b / s.count;
|
|
104
|
+
return _rgb;
|
|
105
|
+
}
|
|
106
|
+
function sampleShaderRGBChromatic(inst, cssPxX, cssPxY, radius) {
|
|
107
|
+
if (!SHARED) {
|
|
108
|
+
_rgb.r = 255;
|
|
109
|
+
_rgb.g = 255;
|
|
110
|
+
_rgb.b = 255;
|
|
111
|
+
return _rgb;
|
|
112
|
+
}
|
|
113
|
+
const m = mapToGlowBuf(inst, cssPxX, cssPxY);
|
|
114
|
+
const { glowPixels: buf, glowPixelsW: W, glowPixelsH: H } = SHARED;
|
|
115
|
+
const r = Math.max(1, 0 | radius);
|
|
116
|
+
const x0 = Math.max(0, m.bx - r);
|
|
117
|
+
const x1 = Math.min(W, m.bx + r + 1);
|
|
118
|
+
const y0 = Math.max(0, m.by - r);
|
|
119
|
+
const y1 = Math.min(H, m.by + r + 1);
|
|
120
|
+
let bestScore = -1;
|
|
121
|
+
_rgb.r = 255;
|
|
122
|
+
_rgb.g = 255;
|
|
123
|
+
_rgb.b = 255;
|
|
124
|
+
for(let py = y0; py < y1; py++){
|
|
125
|
+
const row = py * W;
|
|
126
|
+
for(let px = x0; px < x1; px++){
|
|
127
|
+
const i = (row + px) * 4;
|
|
128
|
+
const rr = buf[i];
|
|
129
|
+
const gg = buf[i + 1];
|
|
130
|
+
const bb = buf[i + 2];
|
|
131
|
+
const maxC = Math.max(rr, gg, bb);
|
|
132
|
+
const minC = Math.min(rr, gg, bb);
|
|
133
|
+
const sat = maxC > 0 ? (maxC - minC) / maxC : 0;
|
|
134
|
+
const score = sat * (0.35 + maxC / 255 * 0.65);
|
|
135
|
+
if (score > bestScore) {
|
|
136
|
+
bestScore = score;
|
|
137
|
+
_rgb.r = rr;
|
|
138
|
+
_rgb.g = gg;
|
|
139
|
+
_rgb.b = bb;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return _rgb;
|
|
144
|
+
}
|
|
145
|
+
export { refreshGlowPixels, sampleShaderLumAt, sampleShaderRGBAt, sampleShaderRGBChromatic };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vertex + fragment shader for the metal-fx effect.
|
|
3
|
+
*
|
|
4
|
+
* Source-of-truth: the `FRAG_SRC` block at L3626–4179 of the canonical
|
|
5
|
+
* `Image loader/index.html` engine. The chromatic / silver / gold presets all
|
|
6
|
+
* run `effectIndex: 1` (Plasma), so we ship ONLY that branch — the original
|
|
7
|
+
* carries 24 effects plus a dot-mode raster grid that this library does not
|
|
8
|
+
* expose. Everything else (warp, snoise, fbm, palette, paletteAlpha, vignette,
|
|
9
|
+
* gamma) is preserved character-for-character so the visible output matches
|
|
10
|
+
* `metal.html` 1:1.
|
|
11
|
+
*
|
|
12
|
+
* Uniforms:
|
|
13
|
+
* u_resolution vec2 — destination pixel buffer (DPR-scaled)
|
|
14
|
+
* u_time float — seconds since boot, JS-side multiplied by speed
|
|
15
|
+
* u_color1..u_color5 vec3 — palette stops (effect 1 uses 5; uniforms 6/7 are
|
|
16
|
+
* still uploaded for forward-compat with effect 24)
|
|
17
|
+
* u_color6, u_color7 vec3 — extra stops, unused at effect 1
|
|
18
|
+
* u_alpha1..u_alpha7 float
|
|
19
|
+
* u_intensity float — amplitude of the wave field
|
|
20
|
+
* u_scale float — noise zoom (smaller = chunkier features)
|
|
21
|
+
* u_direction float — drift angle (radians)
|
|
22
|
+
* u_softness float — kept for shader parity (effect 1 ignores)
|
|
23
|
+
* u_distortion float — warp strength on the sin band field
|
|
24
|
+
* u_complexity float — fbm octaves + frequency multiplier
|
|
25
|
+
* u_shape float — kept for shader parity (effect 1 ignores)
|
|
26
|
+
* u_vignette float — vignette range (0..1)
|
|
27
|
+
* u_vigOpacity float — vignette darkening strength (0..1)
|
|
28
|
+
* u_blur float — sample-radius for the 9-tap blur (0..1)
|
|
29
|
+
* u_shaderOpacity float — final alpha multiplier (0..1)
|
|
30
|
+
*/
|
|
31
|
+
export declare const VERT_SHADER_SRC = "\n attribute vec2 a_position;\n void main() { gl_Position = vec4(a_position, 0.0, 1.0); }\n";
|
|
32
|
+
export declare const FRAG_SHADER_SRC = "\n precision highp float;\n\n uniform vec2 u_resolution;\n uniform float u_time;\n uniform vec3 u_color1, u_color2, u_color3, u_color4, u_color5, u_color6, u_color7;\n uniform float u_alpha1, u_alpha2, u_alpha3, u_alpha4, u_alpha5, u_alpha6, u_alpha7;\n uniform float u_intensity, u_scale, u_direction;\n uniform float u_softness, u_distortion, u_complexity, u_shape;\n uniform float u_vignette, u_vigOpacity, u_blur, u_shaderOpacity;\n\n vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\n vec2 mod289v2(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }\n vec3 permute(vec3 x) { return mod289((x * 34.0 + 1.0) * x); }\n\n float snoise(vec2 v) {\n const vec4 C = vec4(0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439);\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod289v2(i);\n vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));\n vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);\n m = m * m; m = m * m;\n vec3 x_ = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x_) - 0.5;\n vec3 ox = floor(x_ + 0.5);\n vec3 a0 = x_ - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n }\n\n float fbm(vec2 p, float oct) {\n float val = 0.0, amp = 0.5;\n int n = int(oct);\n for (int i = 0; i < 7; i++) {\n if (i >= n) break;\n val += amp * snoise(p);\n p *= 2.0;\n amp *= 0.5;\n }\n return val;\n }\n\n float nfbm(vec2 p) { return fbm(p, 3.0 + u_complexity * 4.0); }\n\n /* 5-stop palette used by effect 1 (Plasma) \u2014 direct port of `palette` from\n * the canonical engine. Stops at t = 0, 0.25, 0.5, 0.75, 1.0. */\n vec3 palette(float t) {\n t = clamp(t, 0.0, 1.0);\n t = t * t * (3.0 - 2.0 * t);\n float k = 64.0;\n float w1 = u_alpha1 * exp(-k * t * t);\n float w2 = u_alpha2 * exp(-k * (t - 0.25) * (t - 0.25));\n float w3 = u_alpha3 * exp(-k * (t - 0.5) * (t - 0.5));\n float w4 = u_alpha4 * exp(-k * (t - 0.75) * (t - 0.75));\n float w5 = u_alpha5 * exp(-k * (t - 1.0) * (t - 1.0));\n float total = w1 + w2 + w3 + w4 + w5 + 0.0001;\n return (u_color1 * w1 + u_color2 * w2 + u_color3 * w3 +\n u_color4 * w4 + u_color5 * w5) / total;\n }\n\n /* Per-pixel alpha that re-introduces transparency when the user dials any\n * palette stop's alpha below 1. Same `paletteAlpha` from the canonical\n * engine. With every preset shipping all-1 alphas, this returns ~1 for every\n * pixel \u2014 but mirroring it keeps custom-preset behaviour identical. */\n float paletteAlpha(float t) {\n t = clamp(t, 0.0, 1.0);\n t = t * t * (3.0 - 2.0 * t);\n float k = 64.0;\n float w1 = u_alpha1 * exp(-k * t * t);\n float w2 = u_alpha2 * exp(-k * (t - 0.25) * (t - 0.25));\n float w3 = u_alpha3 * exp(-k * (t - 0.5) * (t - 0.5));\n float w4 = u_alpha4 * exp(-k * (t - 0.75) * (t - 0.75));\n float w5 = u_alpha5 * exp(-k * (t - 1.0) * (t - 1.0));\n float totalW = w1 + w2 + w3 + w4 + w5 + 0.0001;\n float rawW = exp(-k * t * t)\n + exp(-k * (t - 0.25) * (t - 0.25))\n + exp(-k * (t - 0.5) * (t - 0.5))\n + exp(-k * (t - 0.75) * (t - 0.75))\n + exp(-k * (t - 1.0) * (t - 1.0))\n + 0.0001;\n return totalW / rawW;\n }\n\n vec2 warp(vec2 p, float t) {\n float str = u_distortion * 2.0;\n return vec2(\n nfbm(p + vec2(t * 0.1, 0.0)),\n nfbm(p + vec2(0.0, t * 0.12) + 5.0)\n ) * str;\n }\n\n /* Plasma: four sine bands warped by an FBM field, mapped through the\n * 5-stop palette. Identical to effect 1 in the canonical engine. */\n vec3 computeEffect(vec2 uv, float aspect, float t, float dist, float cpx) {\n vec2 p = (uv - 0.5) * u_scale;\n p.x *= aspect;\n p += vec2(cos(u_direction), sin(u_direction)) * t * 0.15;\n\n float freq = 3.0 + cpx * 8.0;\n float val = 0.0;\n val += sin(p.x * freq + t);\n val += sin(p.y * freq + t * 1.3);\n val += sin((p.x + p.y) * freq * 0.7 + t * 0.7);\n val += sin(length(p) * freq * 0.8 - t * 1.5);\n vec2 w = warp(p, t);\n val += (w.x + w.y) * dist;\n val = val * 0.2 * u_intensity + 0.5;\n\n return palette(clamp(val, 0.0, 1.0));\n }\n\n void main() {\n vec2 uv = gl_FragCoord.xy / u_resolution;\n float aspect = u_resolution.x / u_resolution.y;\n float t = u_time; // JS already multiplied u_time by preset.speed.\n float dist = u_distortion;\n float cpx = u_complexity;\n\n /* 5-tap cross blur (center + cardinal offsets). The chromatic/silver/gold\n * presets all ship with blur=1 so this path is always active. 5 taps\n * instead of the canonical engine's 9 saves ~44% fragment work; the\n * perceptual difference is nil because the output is already soft from\n * the plasma's low spatial frequency and CSS blur on reflections. */\n vec3 col;\n if (u_blur < 0.01) {\n col = computeEffect(uv, aspect, t, dist, cpx);\n } else {\n float r = u_blur * 0.02;\n col = computeEffect(uv, aspect, t, dist, cpx) * 0.4;\n col += computeEffect(uv + vec2( r, 0.0), aspect, t, dist, cpx) * 0.15;\n col += computeEffect(uv + vec2(-r, 0.0), aspect, t, dist, cpx) * 0.15;\n col += computeEffect(uv + vec2(0.0, r), aspect, t, dist, cpx) * 0.15;\n col += computeEffect(uv + vec2(0.0, -r), aspect, t, dist, cpx) * 0.15;\n }\n\n /* Gamma punch \u2014 adds the contrast pop that defines the chromatic\n * highlights. From the canonical engine: `col = pow(col, vec3(1.3))`. */\n col = pow(col, vec3(1.3));\n\n /* Vignette \u2014 soft edge darkening so corners read as recessed. The 40-px\n * scale at the bottom of the formula is hard-coded in the canonical\n * engine; we keep it for visual parity. */\n float edgeDist = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\n float vigPx = 40.0 / min(u_resolution.x, u_resolution.y);\n float vigRange = vigPx * (1.0 + u_vignette * 3.0);\n float vig = edgeDist * edgeDist / (vigRange * vigRange);\n vig = smoothstep(0.0, 1.0, vig);\n col *= mix(1.0, vig, u_vignette * u_vigOpacity);\n\n /* Per-pixel alpha. With all-1 alphas the formula collapses to ~1 but the\n * computation matches the canonical engine so custom presets behave the\n * same. */\n float colorAlpha = (u_alpha1 + u_alpha2 + u_alpha3 + u_alpha4 + u_alpha5) / 5.0;\n if (colorAlpha < 0.999) {\n vec3 c1d = col - u_color1, c2d = col - u_color2, c3d = col - u_color3,\n c4d = col - u_color4, c5d = col - u_color5;\n float prox1 = exp(-8.0 * dot(c1d, c1d));\n float prox2 = exp(-8.0 * dot(c2d, c2d));\n float prox3 = exp(-8.0 * dot(c3d, c3d));\n float prox4 = exp(-8.0 * dot(c4d, c4d));\n float prox5 = exp(-8.0 * dot(c5d, c5d));\n float pTotal = prox1 + prox2 + prox3 + prox4 + prox5 + 0.0001;\n colorAlpha = (prox1 * u_alpha1 + prox2 * u_alpha2 + prox3 * u_alpha3 +\n prox4 * u_alpha4 + prox5 * u_alpha5) / pTotal;\n }\n float alpha = colorAlpha;\n\n /* Touch the unused-at-effect-1 uniforms so GL drivers that complain about\n * declared-but-unread uniforms (some Mali / Adreno builds do) keep them\n * live. The contribution is provably zero. */\n alpha += 0.0 * (u_softness + u_shape +\n u_alpha6 + u_alpha7 +\n u_color6.x + u_color7.x);\n\n gl_FragColor = vec4(col, alpha * u_shaderOpacity);\n }\n";
|
|
33
|
+
/** Compile a single shader stage. Throws with the GL info log on failure. */
|
|
34
|
+
export declare function compileShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader;
|
|
35
|
+
/** Link a vertex + fragment shader pair into a complete program. */
|
|
36
|
+
export declare function linkProgram(gl: WebGLRenderingContext, vert: WebGLShader, frag: WebGLShader): WebGLProgram;
|