@cruxgarden/plasma-ui 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -1
- package/README.md +11 -11
- package/dist/PlasmaProvider.d.ts +6 -6
- package/dist/index.js +12 -12
- package/dist/renderer.d.ts +1 -1
- package/dist/shaders.d.ts +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
**Breaking:** the CSS class on every surface is `.plasma-panel`, was `.plasma-glass`.
|
|
6
|
+
Only matters if you targeted it in your own stylesheet.
|
|
7
|
+
|
|
8
|
+
- The material is called plasma throughout: prop descriptions, docs, and the
|
|
9
|
+
shader's own names. "Glass" is gone except where the readme credits Apple's
|
|
10
|
+
Liquid Glass as prior art.
|
|
11
|
+
- `homepage` points at the site rather than the readme.
|
|
12
|
+
|
|
13
|
+
## 0.1.2
|
|
14
|
+
|
|
15
|
+
- Packaging and metadata only; no runtime change.
|
|
16
|
+
|
|
3
17
|
## 0.1.0
|
|
4
18
|
|
|
5
19
|
Initial release.
|
|
6
20
|
|
|
7
21
|
- `PlasmaProvider`: shared WebGL material with moods, theme, tint/opacity/frost, rim (color, width, highlight, edge line), viscosity/stretch/flow, blend, smoothness, refraction, dispersion, grid/magnet, quality.
|
|
8
|
-
- `Plasma`: any element as a
|
|
22
|
+
- `Plasma`: any element as a plasma surface, with per-surface radius, lean, tint/opacity/frost/elevation, join-aware `padding`, drag with edge/grid snapping, controlled offsets, join events.
|
|
9
23
|
- `usePlasma`: pulse, bump, capability and spring info.
|
|
10
24
|
- CSS frosted fallback when WebGL2 is unavailable; `prefers-reduced-motion` support.
|
|
11
25
|
- `fuse={false}`: surfaces that never blend, bridge, or join - for bars, docks, and fixed chrome.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Plasma UI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Liquid panels for React. Every `<Plasma>` panel joins one shared plasma: surfaces fuse on contact, refract what's behind them, and snap to a grid when dragged. Built for workspace and canvas UIs - tool panels, dashboards, launchers - by [Crux Garden](https://github.com/cruxgarden).
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
@@ -42,7 +42,7 @@ Plasma UI 0.1 is a workspace library, not a full UI system. Know these before ad
|
|
|
42
42
|
- **No layers.** Overlapping surfaces fuse. Dialogs, menus, toasts, and fixed bars must be plain CSS for now (the docs site's own nav shows the pattern).
|
|
43
43
|
- **No clipping in scroll containers.** Plasma inside a scrollable list draws past its edges. Scrolling _inside_ one panel is fine.
|
|
44
44
|
- **No drag or resize handles.** `draggable` moves the whole surface; mark interactive children `data-plasma-nodrag`.
|
|
45
|
-
- **Draws its own background.** Browsers don't let WebGL read the rendered page, so the
|
|
45
|
+
- **Draws its own background.** Browsers don't let WebGL read the rendered page, so the plasma refracts its background layer - the procedural mood field, or any color, image, canvas, or video you pass via `background` - rather than your live DOM. Custom background shaders aren't supported yet.
|
|
46
46
|
- **Rounded rectangles only.** No rotation or arbitrary shapes.
|
|
47
47
|
|
|
48
48
|
All of these are on the roadmap below.
|
|
@@ -54,7 +54,7 @@ Markup stays ordinary HTML. `PlasmaProvider` renders one fixed canvas behind the
|
|
|
54
54
|
1. **Silhouette.** Every registered element reports its box; one shader draws them as a single shape. Corners against a neighbor square off, and blending applies only where surfaces face different ways, so flush panels share a clean outline while gaps and steps get rounded fillets.
|
|
55
55
|
2. **Smoothing.** The silhouette is blurred and traced at its halfway contour, evening out curvature.
|
|
56
56
|
3. **Height.** A heavier blur becomes a height map; its slope drives refraction, so joined panels act as one lens.
|
|
57
|
-
4. **Tint and frost.** Color and translucency spread across the material with the same blur, so different values flow into each other across joins. The background renders once to a texture; two blurred copies serve frosted
|
|
57
|
+
4. **Tint and frost.** Color and translucency spread across the material with the same blur, so different values flow into each other across joins. The background renders once to a texture; two blurred copies serve the frosted plasma.
|
|
58
58
|
5. **Light.** The final pass refracts the background, splits color at the edges, and adds a rim and a pointer highlight.
|
|
59
59
|
|
|
60
60
|
Without WebGL2, `Plasma` falls back to a CSS frosted panel.
|
|
@@ -69,7 +69,7 @@ Without WebGL2, `Plasma` falls back to a CSS frosted panel.
|
|
|
69
69
|
| `background` | `BackgroundSource` | | Any CSS color (luminance drift), image URL (refracted, slow swirl), or an `img`/`canvas`/`video` element - canvas and video update live. Dynamic. Omit for the procedural mood field |
|
|
70
70
|
| `blend` | `number` | mood | Distance (px) at which surfaces start to fuse |
|
|
71
71
|
| `viscosity` | `number` | `0.5` | `0` is watery and bouncy, `1` is slow like syrup; also scales drag and snap springs |
|
|
72
|
-
| `stretch` | `number` | `1` | How far the
|
|
72
|
+
| `stretch` | `number` | `1` | How far the plasma trails behind moving panels; `0` turns it off |
|
|
73
73
|
| `flow` | `number` | `0` | Slow ripple along the edges |
|
|
74
74
|
| `tint` | `string` | `"#ffffff"` | Plasma color (hex) |
|
|
75
75
|
| `opacity` | `number` | `0` | Tint strength, 0 (clear) to 1 (solid color) |
|
|
@@ -128,12 +128,12 @@ const dusk: Mood = {
|
|
|
128
128
|
|
|
129
129
|
## Motion feel
|
|
130
130
|
|
|
131
|
-
Each surface is a spring chasing its element, and the drawn
|
|
131
|
+
Each surface is a spring chasing its element, and the drawn plasma always covers the element. Moving panels leave a trailing stretch; stopping ones overshoot before settling. Scrolling doesn't count as motion.
|
|
132
132
|
|
|
133
133
|
```tsx
|
|
134
134
|
<PlasmaProvider viscosity={0.1} stretch={1.3} flow={0.6} /> // water
|
|
135
135
|
<PlasmaProvider viscosity={0.85} stretch={1.8} /> // honey
|
|
136
|
-
<PlasmaProvider stretch={0} /> //
|
|
136
|
+
<PlasmaProvider stretch={0} /> // the plasma tracks panels exactly
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
`flow` ripples the outline, so leave it at `0` where flush edges should stay perfectly straight.
|
|
@@ -149,13 +149,13 @@ Each surface is a spring chasing its element, and the drawn glass always covers
|
|
|
149
149
|
<Plasma tint="#ff5fa2" opacity={0.3} />
|
|
150
150
|
</PlasmaProvider>
|
|
151
151
|
|
|
152
|
-
// plain
|
|
152
|
+
// plain plasma: no colored rim, just the edge line
|
|
153
153
|
<PlasmaProvider rim={0} />
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
## Guidelines
|
|
157
157
|
|
|
158
|
-
- Use
|
|
158
|
+
- Use plasma for containers: panels, docks, cards, dialogs. Small controls read better as regular HTML on top.
|
|
159
159
|
- Place surfaces either flush (they become one piece) or further apart than the blend distance. Smaller gaps render as liquid bridging.
|
|
160
160
|
- Up to `maxSurfaces` (default 16) draw at once; offscreen ones are skipped first. Two render passes loop over every slot per pixel, so raise it only as far as you need.
|
|
161
161
|
- Lean and pulses use the CSS `translate` and `scale` properties, and drag uses `transform`, so they compose with each other. Avoid setting those on `Plasma` elements yourself.
|
|
@@ -165,9 +165,9 @@ Each surface is a spring chasing its element, and the drawn glass always covers
|
|
|
165
165
|
|
|
166
166
|
In priority order. Not a schedule.
|
|
167
167
|
|
|
168
|
-
1. **Layers** - independent materials that stack instead of fusing, for dialogs, menus, and fixed chrome over
|
|
168
|
+
1. **Layers** - independent materials that stack instead of fusing, for dialogs, menus, and fixed chrome over plasma.
|
|
169
169
|
2. **Drag handles and resize** - `handle` prop so panel content stays fully interactive; edge resize with grid snapping.
|
|
170
|
-
3. **Scroll clipping** -
|
|
170
|
+
3. **Scroll clipping** - plasma confined to scrollable containers.
|
|
171
171
|
4. **Pluggable backgrounds** - colors, images, and live canvas/video shipped in 0.1 (`background` prop); custom shaders next.
|
|
172
172
|
5. **Shapes** - rotation and non-rectangular outlines.
|
|
173
173
|
|
|
@@ -225,7 +225,7 @@ Plasma UI is built on well-known graphics and simulation techniques:
|
|
|
225
225
|
- **Blobby surfaces / metaballs** - the fuse-on-contact behavior descends from Jim Blinn's [_A Generalization of Algebraic Surface Drawing_](https://dl.acm.org/doi/10.1145/357306.357310) (1982).
|
|
226
226
|
- **Signed distance fields** - the material is drawn with 2D SDFs combined by smooth minimum, per Inigo Quilez's [2D distance functions](https://iquilezles.org/articles/distfunctions2d/) and [smooth minimum](https://iquilezles.org/articles/smin/) articles; the procedural background uses his [fBM](https://iquilezles.org/articles/fbm/) construction.
|
|
227
227
|
- **Spring integration** - panel motion uses semi-implicit Euler with fixed substeps, in the spirit of Glenn Fiedler's [_Integration Basics_](https://gafferongames.com/post/integration_basics/).
|
|
228
|
-
- **
|
|
228
|
+
- **The optical treatment** (refraction, dispersion, frost) is an original WebGL take on the direction popularized by Apple's [Liquid Glass](https://developer.apple.com/design/human-interface-guidelines/materials) material.
|
|
229
229
|
|
|
230
230
|
## License
|
|
231
231
|
|
package/dist/PlasmaProvider.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface PlasmaProviderProps {
|
|
|
25
25
|
edgeLine?: number;
|
|
26
26
|
/** How thick the material feels: 0 is watery and bouncy, 1 is slow like syrup. Also scales drag and snap springs. Default 0.5. */
|
|
27
27
|
viscosity?: number;
|
|
28
|
-
/** How far the
|
|
28
|
+
/** How far the plasma trails and stretches behind moving panels. 0 turns it off. Default 1. */
|
|
29
29
|
stretch?: number;
|
|
30
30
|
/** Slow ripple along the edges. Default 0 (still edges). */
|
|
31
31
|
flow?: number;
|
|
@@ -35,9 +35,9 @@ export interface PlasmaProviderProps {
|
|
|
35
35
|
radius?: number;
|
|
36
36
|
/** Plasma tint color (hex). Default "#ffffff". */
|
|
37
37
|
tint?: string;
|
|
38
|
-
/** Tint strength from 0 (clear
|
|
38
|
+
/** Tint strength from 0 (clear) to 1 (solid color). Default 0. */
|
|
39
39
|
opacity?: number;
|
|
40
|
-
/** Translucency from 0 (clear
|
|
40
|
+
/** Translucency from 0 (clear) to 1 (frosted). Default 0. */
|
|
41
41
|
frost?: number;
|
|
42
42
|
/** How high surfaces float: 0 sits flat with no shadow, 1 floats high with a deep soft shadow. Dragged surfaces raise automatically. Default 0.35. */
|
|
43
43
|
elevation?: number;
|
|
@@ -47,13 +47,13 @@ export interface PlasmaProviderProps {
|
|
|
47
47
|
pointerDrop?: boolean;
|
|
48
48
|
/** Decorative drops orbiting near the bottom right. Default false. */
|
|
49
49
|
ambientDrops?: boolean;
|
|
50
|
-
/** Grid cell size used when draggable
|
|
50
|
+
/** Grid cell size used when a draggable panel snaps. Default 24. */
|
|
51
51
|
grid?: number;
|
|
52
52
|
/** Edge latch distance for snapping. Default 40. */
|
|
53
53
|
magnet?: number;
|
|
54
54
|
/** Maximum device pixel ratio for the canvas. Default 1.25. */
|
|
55
55
|
quality?: number;
|
|
56
|
-
/** Maximum visible
|
|
56
|
+
/** Maximum visible plasma surfaces. Compiled into the shaders, so it is fixed for the provider's lifetime; more surfaces cost GPU time. Default 16. */
|
|
57
57
|
maxSurfaces?: number;
|
|
58
58
|
/** z-index of the fixed canvas. Default -1 (behind content). */
|
|
59
59
|
zIndex?: number;
|
|
@@ -66,7 +66,7 @@ export interface PlasmaContextValue {
|
|
|
66
66
|
frost: number;
|
|
67
67
|
/** Provider-level default corner radius. */
|
|
68
68
|
radius: number;
|
|
69
|
-
/** False when WebGL2 is unavailable; <Plasma> falls back to CSS
|
|
69
|
+
/** False when WebGL2 is unavailable; <Plasma> falls back to a CSS frosted panel. */
|
|
70
70
|
supported: boolean;
|
|
71
71
|
grid: number;
|
|
72
72
|
magnet: number;
|
package/dist/index.js
CHANGED
|
@@ -185,7 +185,7 @@ out vec4 o;
|
|
|
185
185
|
vec3 pal(float t){ return .5 + .5*cos(6.2831*(t + vec3(0., .33, .67))); }
|
|
186
186
|
float H(vec2 uv){ return texture(uH, uv).r; }
|
|
187
187
|
vec2 uvAt(vec2 q){ return vec2(q.x, uRes.y - q.y) / uRes; }
|
|
188
|
-
// background seen through
|
|
188
|
+
// background seen through plasma with frost f: sharp, then medium, then heavy blur
|
|
189
189
|
vec3 seen(vec2 q, float f){
|
|
190
190
|
vec2 u = uvAt(q);
|
|
191
191
|
vec3 sharp = texture(uBg, u).rgb;
|
|
@@ -253,7 +253,7 @@ void main(){
|
|
|
253
253
|
seen(p + off*(1.-disp), fr).b
|
|
254
254
|
);
|
|
255
255
|
refr = mix(refr, vec3(dot(refr, vec3(.333))), .18) * mix(1.08, .9, uLight) + .03*(1.-uLight);
|
|
256
|
-
// frosted
|
|
256
|
+
// frosted: milkier and a little brighter
|
|
257
257
|
refr = mix(refr, mix(refr, vec3(dot(refr, vec3(.333))), .25) * mix(1.12, .97, uLight) + mix(.05, .03, uLight), fr);
|
|
258
258
|
float hl = 1. - .55*uLight;
|
|
259
259
|
|
|
@@ -273,16 +273,16 @@ void main(){
|
|
|
273
273
|
if (uRimMode > .5 && uRimMode < 1.5) rimCol = uRimColor * facing * 1.4;
|
|
274
274
|
else if (uRimMode > 1.5) rimCol = tcol * facing * 1.4;
|
|
275
275
|
|
|
276
|
-
vec3
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
vec3 plasma = refr;
|
|
277
|
+
plasma += rimCol * fres * .45 * hl * uRim;
|
|
278
|
+
plasma += vec3(1.) * spec * .75 * hl * uSpec;
|
|
279
279
|
// faint shimmer across the body; fades out as the tint becomes opaque
|
|
280
|
-
|
|
280
|
+
plasma += pal(uTime*.04 + p.y/900. + uEnergy*.3) * .05 * lift * (1.+uEnergy*2.) * hl * (1. - talpha);
|
|
281
281
|
vec3 hairCol = uRimMode > .5 ? mix(vec3(1.), rimCol / 1.4, .6) : vec3(.9,.95,1.);
|
|
282
|
-
|
|
282
|
+
plasma += hairCol * (1.-smoothstep(0., 1.6, abs(sd - .7))) * .4 * hl * uHair;
|
|
283
283
|
|
|
284
284
|
float a = smoothstep(-.8, .8, sd);
|
|
285
|
-
col = mix(col,
|
|
285
|
+
col = mix(col, plasma, a);
|
|
286
286
|
grain = 1. - a; // grain is background-only; panels stay clean
|
|
287
287
|
}
|
|
288
288
|
col += (hash(p + uTime) - .5) * .025 * grain;
|
|
@@ -1024,15 +1024,15 @@ var PlasmaContext = createContext({
|
|
|
1024
1024
|
});
|
|
1025
1025
|
var usePlasma = () => useContext(PlasmaContext);
|
|
1026
1026
|
var FALLBACK_CSS = `
|
|
1027
|
-
.plasma-
|
|
1027
|
+
.plasma-panel{box-sizing:border-box}
|
|
1028
1028
|
.plasma-fallback{
|
|
1029
1029
|
background:linear-gradient(160deg,rgb(255 255 255/.14),rgb(255 255 255/.05));
|
|
1030
1030
|
-webkit-backdrop-filter:blur(18px) saturate(160%);backdrop-filter:blur(18px) saturate(160%);
|
|
1031
1031
|
border:1px solid rgb(255 255 255/.22);
|
|
1032
1032
|
box-shadow:inset 0 1px 0 rgb(255 255 255/.25),0 20px 40px -20px rgb(0 0 0/.5);
|
|
1033
1033
|
}
|
|
1034
|
-
.plasma-
|
|
1035
|
-
.plasma-
|
|
1034
|
+
.plasma-panel[data-plasma-draggable]{touch-action:none;cursor:grab}
|
|
1035
|
+
.plasma-panel[data-plasma-dragging]{cursor:grabbing;user-select:none}
|
|
1036
1036
|
`;
|
|
1037
1037
|
function useReducedMotion() {
|
|
1038
1038
|
const [reduced, setReduced] = useState(false);
|
|
@@ -1483,7 +1483,7 @@ var Plasma = forwardRef(function Plasma2({
|
|
|
1483
1483
|
e.preventDefault();
|
|
1484
1484
|
settle({ x: dest.current.x + d[0], y: dest.current.y + d[1] });
|
|
1485
1485
|
};
|
|
1486
|
-
const classes = ["plasma-
|
|
1486
|
+
const classes = ["plasma-panel", plasma.supported ? "" : "plasma-fallback", className].filter(Boolean).join(" ");
|
|
1487
1487
|
return /* @__PURE__ */ jsx2(
|
|
1488
1488
|
Comp,
|
|
1489
1489
|
{
|
package/dist/renderer.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface RendererSettings {
|
|
|
25
25
|
stretch: number;
|
|
26
26
|
/** Slow ripple along the outline. 0 = still edges. */
|
|
27
27
|
flow: number;
|
|
28
|
-
/** Default
|
|
28
|
+
/** Default plasma tint (hex). */
|
|
29
29
|
tint: string;
|
|
30
30
|
/** Default tint strength, 0 (clear) to 1 (solid color). */
|
|
31
31
|
opacity: number;
|
package/dist/shaders.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Default maximum
|
|
1
|
+
/** Default maximum plasma shapes drawn at once (offscreen shapes are culled first). */
|
|
2
2
|
export declare const DEFAULT_MAX_SHAPES = 16;
|
|
3
3
|
/** Maximum concurrent pulses. */
|
|
4
4
|
export declare const MAX_PULSES = 4;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cruxgarden/plasma-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Liquid panels for React: every panel is one shared plasma - they fuse on contact, refract, and snap to a grid.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "esbuild src/index.ts --bundle --format=esm --jsx=automatic --target=es2020 --external:react --external:react-dom --outfile=dist/index.js && tsc",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"cruxgarden",
|
|
54
54
|
"react",
|
|
55
55
|
"webgl",
|
|
56
|
-
"
|
|
56
|
+
"panels",
|
|
57
57
|
"ui",
|
|
58
58
|
"animation"
|
|
59
59
|
],
|