@cruxgarden/plasma-ui 0.1.1

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.
@@ -0,0 +1,40 @@
1
+ export interface Mood {
2
+ /** Background colors: deep base, mid tone, accent. Hex strings. */
3
+ colors: [string, string, string];
4
+ /** How far apart (px) surfaces start to fuse. */
5
+ blend: number;
6
+ /** Spring used for drag release, snapping, and controlled offsets. */
7
+ spring: {
8
+ stiffness: number;
9
+ damping: number;
10
+ };
11
+ }
12
+ export declare const moods: {
13
+ tidal: {
14
+ colors: [string, string, string];
15
+ blend: number;
16
+ spring: {
17
+ stiffness: number;
18
+ damping: number;
19
+ };
20
+ };
21
+ aurora: {
22
+ colors: [string, string, string];
23
+ blend: number;
24
+ spring: {
25
+ stiffness: number;
26
+ damping: number;
27
+ };
28
+ };
29
+ ember: {
30
+ colors: [string, string, string];
31
+ blend: number;
32
+ spring: {
33
+ stiffness: number;
34
+ damping: number;
35
+ };
36
+ };
37
+ };
38
+ export type MoodName = keyof typeof moods;
39
+ export declare function resolveMood(mood: MoodName | Mood): Mood;
40
+ export declare function hexToRgb(hex: string): [number, number, number];
@@ -0,0 +1,143 @@
1
+ import { Box } from "./snap";
2
+ export interface RendererSettings {
3
+ colors: [string, string, string];
4
+ blend: number;
5
+ refraction: number;
6
+ dispersion: number;
7
+ rim: number;
8
+ smoothness: number;
9
+ pointerDrop: boolean;
10
+ ambientDrops: boolean;
11
+ theme: "auto" | "light" | "dark";
12
+ quality: number;
13
+ reducedMotion: boolean;
14
+ /** Rim color: "iridescent", "tint" (each surface's tint), or a hex color. */
15
+ rimColor: string;
16
+ /** Rim width multiplier. */
17
+ rimWidth: number;
18
+ /** Pointer-facing highlight strength. */
19
+ highlight: number;
20
+ /** Thin edge line strength. */
21
+ edgeLine: number;
22
+ /** 0 = watery and bouncy, 1 = thick and slow. */
23
+ viscosity: number;
24
+ /** How far the surface trails behind moving panels. 0 = no trailing. */
25
+ stretch: number;
26
+ /** Slow ripple along the outline. 0 = still edges. */
27
+ flow: number;
28
+ /** Default glass tint (hex). */
29
+ tint: string;
30
+ /** Default tint strength, 0 (clear) to 1 (solid color). */
31
+ opacity: number;
32
+ /** Default translucency, 0 (clear) to 1 (frosted). */
33
+ frost: number;
34
+ /** Default elevation, 0 (flat, no shadow) to 1 (floating high). */
35
+ elevation: number;
36
+ /** Maximum visible surfaces, compiled into the shaders (fixed at creation). */
37
+ maxSurfaces: number;
38
+ /** Background: CSS color, image URL, or a live img/canvas/video source (null for the procedural mood field). */
39
+ background: BackgroundSource | null;
40
+ }
41
+ /** Anything the background can be: a CSS color string, an image URL, or an element to sample (canvas and video update live). */
42
+ export type BackgroundSource = string | HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | ImageBitmap;
43
+ export interface ShapeOptions {
44
+ radius: number;
45
+ lean: number;
46
+ /** Per-shape tint override (hex). */
47
+ tint?: string | null;
48
+ /** Per-shape tint strength override. */
49
+ opacity?: number | null;
50
+ /** Per-shape frost override. */
51
+ frost?: number | null;
52
+ /** Per-shape elevation override. */
53
+ elevation?: number | null;
54
+ /** When false, this surface never blends, bridges, or joins with others. Default true. */
55
+ fuse?: boolean;
56
+ }
57
+ /** Handle returned by `register`, used by <Plasma>. */
58
+ export interface JoinedSides {
59
+ top: boolean;
60
+ right: boolean;
61
+ bottom: boolean;
62
+ left: boolean;
63
+ }
64
+ export interface ShapeHandle {
65
+ id: number;
66
+ update(o: Partial<ShapeOptions>): void;
67
+ setLayoutBox(fn: (() => Box) | null): void;
68
+ setDragging(on: boolean): void;
69
+ leanOffset(): {
70
+ x: number;
71
+ y: number;
72
+ };
73
+ isJoined(): boolean;
74
+ remove(): void;
75
+ }
76
+ export declare class PlasmaRenderer {
77
+ private gl;
78
+ private canvas;
79
+ private progs;
80
+ private rtA;
81
+ private rtB;
82
+ private rtC;
83
+ private rtT;
84
+ private rtFr;
85
+ private rtBg;
86
+ private rtBgM;
87
+ private rtBgH;
88
+ private mrtFb;
89
+ private imgTex;
90
+ private imgRes;
91
+ private imgSrc;
92
+ private bgColor;
93
+ private srcEl;
94
+ private floatOK;
95
+ private recs;
96
+ private nextId;
97
+ private raf;
98
+ private last;
99
+ private time;
100
+ private dpr;
101
+ private energy;
102
+ private mouse;
103
+ private pulses;
104
+ private pulseIdx;
105
+ private colors;
106
+ private colorTarget;
107
+ private blend;
108
+ private lightQuery;
109
+ private max;
110
+ private P;
111
+ private R;
112
+ private F;
113
+ private T;
114
+ private FR;
115
+ private EL;
116
+ private SOLO;
117
+ private tintCache;
118
+ private RP;
119
+ settings: RendererSettings;
120
+ /** Returns null when WebGL2 is unavailable. */
121
+ static create(canvas: HTMLCanvasElement, settings: RendererSettings): PlasmaRenderer | null;
122
+ private constructor();
123
+ private loadBackground;
124
+ configure(s: RendererSettings, immediate?: boolean): void;
125
+ register(el: HTMLElement, o: ShapeOptions, onJoin?: (j: boolean) => void, onSides?: (sides: JoinedSides) => void): ShapeHandle;
126
+ /** Layout boxes of all shapes (lean removed; animating draggables report their destination). */
127
+ layoutBoxes(excludeId?: number, fusingOnly?: boolean): Box[];
128
+ private layoutBoxOf;
129
+ /** Send a pulse through the material from a viewport point. */
130
+ pulse(x: number, y: number, strength?: number): void;
131
+ /** Raise the material's energy (brightens contours and color); it decays on its own. */
132
+ bump(e: number): void;
133
+ destroy(): void;
134
+ private onPointer;
135
+ private onLeave;
136
+ private resize;
137
+ private isLight;
138
+ private frame;
139
+ private draw;
140
+ private rgb;
141
+ private program;
142
+ private target;
143
+ }
@@ -0,0 +1,14 @@
1
+ /** Default maximum glass shapes drawn at once (offscreen shapes are culled first). */
2
+ export declare const DEFAULT_MAX_SHAPES = 16;
3
+ /** Maximum concurrent pulses. */
4
+ export declare const MAX_PULSES = 4;
5
+ export declare const vert = "#version 300 es\nin vec2 p; void main(){ gl_Position = vec4(p, 0., 1.); }";
6
+ export interface ShaderSet {
7
+ maskFrag: string;
8
+ tintFrag: string;
9
+ blurFrag: string;
10
+ bgFrag: string;
11
+ compFrag: string;
12
+ }
13
+ /** Build the shader set for a given surface budget (compiled into the programs). */
14
+ export declare function makeShaders(MAX_SHAPES: number): ShaderSet;
package/dist/snap.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ export interface Box {
2
+ l: number;
3
+ t: number;
4
+ w: number;
5
+ h: number;
6
+ }
7
+ export interface SnapOptions {
8
+ /** Grid cell size in px. 0 disables the grid. */
9
+ grid: number;
10
+ /** Distance (px) at which an edge latches onto a neighbor's edge. */
11
+ magnet: number;
12
+ /** Area the box must stay inside; also the grid origin. */
13
+ bounds: Box;
14
+ /** Inset from the bounds edges. */
15
+ inset?: number;
16
+ }
17
+ /**
18
+ * Snap a proposed box position. Edges first latch onto nearby panels
19
+ * (aligned or butted), otherwise fall to the grid. Overlaps are pushed out
20
+ * so the result sits flush against its neighbor.
21
+ */
22
+ export declare function snapBox(b: Box, others: Box[], o: SnapOptions): {
23
+ l: number;
24
+ t: number;
25
+ };
26
+ /** Gap between two boxes (negative when overlapping). */
27
+ export declare function boxGap(a: Box, b: Box): number;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Minimal animated value + spring, replacing the previous Motion dependency.
3
+ * A SpringValue tracks velocity from recent set() calls (so a pointer drag
4
+ * carries momentum into the spring), and animateSpring() integrates a damped
5
+ * spring on requestAnimationFrame.
6
+ */
7
+ export interface SpringValue {
8
+ get(): number;
9
+ set(v: number): void;
10
+ /** Subscribe to changes; returns an unsubscribe function. */
11
+ on(fn: (v: number) => void): () => void;
12
+ /** Velocity in px/s, estimated from recent set() calls. */
13
+ getVelocity(): number;
14
+ }
15
+ export interface SpringHandle {
16
+ stop(): void;
17
+ }
18
+ export declare function springValue(initial: number): SpringValue;
19
+ export interface SpringOptions {
20
+ stiffness: number;
21
+ damping: number;
22
+ velocity?: number;
23
+ }
24
+ /** Animate a SpringValue to a target with a damped spring. */
25
+ export declare function animateSpring(value: SpringValue, to: number, o: SpringOptions): SpringHandle;
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@cruxgarden/plasma-ui",
3
+ "version": "0.1.1",
4
+ "description": "A liquid glass workspace for React: panels share one WebGL material - they fuse on contact, refract, and snap to a grid.",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "esbuild src/index.ts --bundle --format=esm --jsx=automatic --target=es2020 --external:react --external:react-dom --outfile=dist/index.js && tsc",
8
+ "build:site": "node site/build.mjs",
9
+ "typecheck": "tsc --noEmit",
10
+ "test": "node --test tests/*.test.mjs",
11
+ "prepublishOnly": "npm run typecheck && npm test && npm run build",
12
+ "format": "prettier --write .",
13
+ "verify": "npm run typecheck && npm test && npm run build",
14
+ "publish:patch": "npm version patch && npm publish && git push && git push --tags",
15
+ "publish:minor": "npm version minor && npm publish && git push && git push --tags",
16
+ "publish:major": "npm version major && npm publish && git push && git push --tags",
17
+ "build:example": "node examples/workspace/build.mjs",
18
+ "build:pages": "node scripts/build-pages.mjs"
19
+ },
20
+ "author": "The Keeper",
21
+ "license": "MIT",
22
+ "devDependencies": {
23
+ "@types/react": "^18.3.31",
24
+ "@types/react-dom": "^18.3.7",
25
+ "esbuild": "^0.25.12",
26
+ "prettier": "^3.5.0",
27
+ "react": "^18.3.1",
28
+ "react-dom": "^18.3.1",
29
+ "typescript": "^5.9.3"
30
+ },
31
+ "type": "module",
32
+ "module": "dist/index.js",
33
+ "types": "dist/index.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/index.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "LICENSE",
44
+ "CHANGELOG.md"
45
+ ],
46
+ "sideEffects": false,
47
+ "peerDependencies": {
48
+ "react": ">=18",
49
+ "react-dom": ">=18"
50
+ },
51
+ "keywords": [
52
+ "crux",
53
+ "cruxgarden",
54
+ "react",
55
+ "webgl",
56
+ "glass",
57
+ "ui",
58
+ "animation"
59
+ ],
60
+ "homepage": "https://github.com/CruxGarden/plasma-ui#readme",
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "git+https://github.com/CruxGarden/plasma-ui.git"
64
+ },
65
+ "bugs": {
66
+ "url": "https://github.com/CruxGarden/plasma-ui/issues"
67
+ },
68
+ "engines": {
69
+ "node": ">=22.0.0"
70
+ },
71
+ "publishConfig": {
72
+ "access": "public"
73
+ }
74
+ }