lgimgui 0.1.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 +111 -0
- package/demo/public/materials.json +809 -0
- package/package.json +39 -0
- package/src/core/curve.js +71 -0
- package/src/core/frame.js +272 -0
- package/src/core/geometry.js +61 -0
- package/src/core/ids.js +31 -0
- package/src/core/input.js +294 -0
- package/src/core/layout.js +220 -0
- package/src/core/spring.js +113 -0
- package/src/core/state.js +54 -0
- package/src/core/theme.js +77 -0
- package/src/index.js +221 -0
- package/src/material/adapt.js +72 -0
- package/src/material/material.js +131 -0
- package/src/material/optics.js +49 -0
- package/src/material/params.js +83 -0
- package/src/material/presets.js +291 -0
- package/src/render/batch.js +236 -0
- package/src/render/context.js +138 -0
- package/src/render/glyphs.js +177 -0
- package/src/render/probe.js +64 -0
- package/src/render/pyramid.js +48 -0
- package/src/render/renderer.js +296 -0
- package/src/render/shaders/color.js +58 -0
- package/src/render/shaders/glass.js +241 -0
- package/src/render/shaders/ui.js +194 -0
- package/src/widgets/composite.js +355 -0
- package/src/widgets/controls.js +545 -0
- package/src/widgets/desktop.js +144 -0
- package/src/widgets/glass.js +326 -0
- package/src/widgets/primitives.js +355 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# lgimgui
|
|
2
|
+
|
|
3
|
+
An immediate mode GUI for the browser whose navigation layer is liquid glass. Rendering is WebGL2. The optical model is a port of [liquid-glass-studio](https://github.com/iyinchao/liquid-glass-studio) (the same parameters, ranges, and formulas), with the role presets tuned in `lgwm`. The composition rules come from Apple's Liquid Glass guidance and are enforced by the library rather than left to the caller.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install
|
|
7
|
+
make client # demo on http://localhost:5173
|
|
8
|
+
make test
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The demo is a desktop: a dock, windows with sidebars and scroll edges, a merged toolbar group, menus that pop from their buttons, a slider whose thumb lifts into clear glass while held, and a material editor written with the library itself that edits every studio parameter live for each role.
|
|
12
|
+
|
|
13
|
+
## Writing a frame
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import { createUI } from 'lgimgui'
|
|
17
|
+
|
|
18
|
+
const ui = createUI(canvas)
|
|
19
|
+
const state = { volume: 0.6, wifi: true, tab: 0 }
|
|
20
|
+
|
|
21
|
+
ui.start(() => {
|
|
22
|
+
ui.image(wallpaper, { x: 0, y: 0, w: 'fill', h: 'fill' })
|
|
23
|
+
|
|
24
|
+
const win = ui.window({ key: 'settings', title: 'Settings', x: 80, y: 60, w: 520, h: 360 }, () => {
|
|
25
|
+
ui.column({ pad: 16, gap: 12 }, () => {
|
|
26
|
+
state.tab = ui.segmented(['General', 'Display'], state.tab, { key: 'tab' })
|
|
27
|
+
state.volume = ui.slider(state.volume, { key: 'volume' })
|
|
28
|
+
state.wifi = ui.toggle(state.wifi, { key: 'wifi' })
|
|
29
|
+
if (ui.button('Done', { primary: true })) close()
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
ui.glass({ key: 'dock', role: 'dock', shape: 'pill', direction: 'row', gap: 8, pad: 8, y: 800, alignSelf: 'center' }, () => {
|
|
34
|
+
if (ui.button(null, { key: 'mail', icon: 'grid' })) openMail()
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Every call records a node for this frame. After the frame callback returns the tree is measured and positioned, painted into strata, rendered, and hit tested for the next frame. Widget state (springs, scroll offsets, carets, menu open flags) lives in a store keyed by id and is collected when the id stops appearing.
|
|
40
|
+
|
|
41
|
+
### Layout
|
|
42
|
+
|
|
43
|
+
Containers take `direction` (`row`, `column`, `stack`), `gap`, `pad`, `align` (`start`, `center`, `end`, `stretch`), `justify` (`start`, `center`, `end`, `between`). Children size with `w`/`h` as a number, `'fill'`, or omitted for intrinsic size, plus `fr` to share leftover space. Setting `x` or `y` positions a child absolutely inside its parent; `alignSelf` aligns the other axis.
|
|
44
|
+
|
|
45
|
+
### Ids
|
|
46
|
+
|
|
47
|
+
Ids are paths built from the enclosing containers and the widget label. Pass `key` when labels repeat or change. Repeated keys in one scope are disambiguated in order, so a list of identical buttons still works as long as its order is stable.
|
|
48
|
+
|
|
49
|
+
### Glass
|
|
50
|
+
|
|
51
|
+
`ui.glass(props, fn)` draws one glass element and lays out its children as overlays on it.
|
|
52
|
+
|
|
53
|
+
- `role`: `window`, `pane`, `toolbar`, `menu`, `control`, `thumb`, `dock`, `clear`, `thin`, `studio`. A role is a material preset; every preset exposes all studio parameters in `ui.materials[role]`, with independent `unfocused` and `dark` variants.
|
|
54
|
+
- `variant`: `regular` (default) adapts to its backdrop; `clear` never adapts, always dims, and forces bright foreground.
|
|
55
|
+
- `shape`: `pill`, `circle`, or a numeric `radius` with `roundness` (2 is round, 5 is the squircle default).
|
|
56
|
+
- `merge: true` turns each direct child into a shape of one smooth-min union with `mergeRate` pixels of bridging.
|
|
57
|
+
- `focused: false` switches to the unfocused material so a whole hierarchy recedes together.
|
|
58
|
+
- `tint: [r, g, b, a]` applies a semantic tint mapped to backdrop brightness.
|
|
59
|
+
- `surface`: `small` flips light and dark with the backdrop; `large` holds polarity.
|
|
60
|
+
- `lift: true` lets a transient element render as glass while inside glass.
|
|
61
|
+
|
|
62
|
+
Controls called inside glass paint as fills, vibrancy, and strokes on that glass. Controls called outside glass bring their own glass backing in the `control` role. Nesting `ui.glass` inside `ui.glass` without `lift` throws.
|
|
63
|
+
|
|
64
|
+
### Widgets
|
|
65
|
+
|
|
66
|
+
`text`, `label`, `heading`, `icon`, `image`, `rect`, `separator`, `spacer`, `box`, `row`, `column`, `stack`, `scroll`, `popup`, `button`, `iconButton`, `capsule`, `toolbar`, `trafficLights`, `toggle`, `slider`, `segmented`, `progress`, `item`, `menu`, `menuButton`, `textInput`, `pane`, `window`, `dragHandle`, `hitArea`.
|
|
67
|
+
|
|
68
|
+
Interactive widgets return their result immediately: `button` returns whether it was clicked, `slider` and `toggle` return the new value, `menuButton` returns the chosen item, `window` returns `{ closed, moved, dx, dy, focusRequested, minimized, zoomed }`.
|
|
69
|
+
|
|
70
|
+
`scroll` accepts `edge: { size }` to fade its content out at the edges; the content's own pixels become transparent, nothing is painted over them.
|
|
71
|
+
|
|
72
|
+
### Windows and the desktop
|
|
73
|
+
|
|
74
|
+
`ui.desktop()` returns a window manager. Register each window every frame with `desktop.window(key, spec, fn)` and call `desktop.draw()` once; the manager owns geometry (with cascade placement for new keys), z-order, focus (a press on nothing unfocuses everything), drag, resize, close, minimize, and zoom. `spec` takes `title` or a `toolbar` callback, `sidebar` and `sidebarWidth`, `w`, `h`, optional `x`, `y`, `open: false` to start hidden, `minSize`, and `resizable`. `desktop.open(key)`, `close`, `toggle`, `isOpen`, and `focused` drive it from the outside (a dock, a menu).
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const desktop = ui.desktop()
|
|
78
|
+
|
|
79
|
+
ui.start(() => {
|
|
80
|
+
desktop.window('sliders', { title: 'Sliders', w: 360, h: 200 }, () => {
|
|
81
|
+
state.volume = ui.slider(state.volume, { key: 'volume' })
|
|
82
|
+
})
|
|
83
|
+
desktop.draw()
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`ui.window` underneath composes the sheet: traffic lights at the corner, a full-height `pane` for the sidebar, a toolbar band (also the drag region), and the content below. Buttons and capsules inside a toolbar take the `toolbar` material; everything else inside a window is a fill.
|
|
88
|
+
|
|
89
|
+
### Settings
|
|
90
|
+
|
|
91
|
+
`ui.settings.reduceTransparency`, `ui.settings.increaseContrast`, and `ui.settings.reduceMotion` are material modifiers applied everywhere.
|
|
92
|
+
|
|
93
|
+
## How rendering works
|
|
94
|
+
|
|
95
|
+
Each frame is a list of strata. Stratum 0 is the content layer. A glass element samples the completed stratum beneath it and writes itself and its overlays into the stratum above. Glass elements that overlap earlier glass are pushed up a stratum automatically, so a higher window sees a lower window only as flattened pixels and two independent windows never refract each other. Popups and lifted elements follow the same rule.
|
|
96
|
+
|
|
97
|
+
Per stratum the renderer draws the content, the shadows of the glass above (into the backdrop, so the rim refracts them like the studio does), builds a gaussian mip pyramid of the result, and moves on. Blur radius selects a fractional pyramid level, so every element can carry its own blur without extra passes. A tiny level of each stratum's pyramid is read back asynchronously every frame and feeds the adaptation of small Regular glass sitting on it: polarity with hysteresis, shadow depth over detail, and tint opacity.
|
|
98
|
+
|
|
99
|
+
Text is rasterized into a glyph atlas at device pixel ratio and drawn sharp above the glass. UI fills, strokes, images, glyphs, and gradients go through one instanced batch.
|
|
100
|
+
|
|
101
|
+
## Project layout
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
src/index.js createUI
|
|
105
|
+
src/core/ ids, state, springs, input, layout, frame driver, theme
|
|
106
|
+
src/material/ studio parameter schema, role presets, material transforms, backdrop adaptation, optics
|
|
107
|
+
src/render/ WebGL2 context, batch, glyph atlas, blur pyramid, probe, renderer, shaders
|
|
108
|
+
src/widgets/ glass, primitives, controls, composite widgets, desktop manager
|
|
109
|
+
demo/ the desktop demo, material editor, and showcase windows
|
|
110
|
+
test/ vitest suites for the pure modules
|
|
111
|
+
```
|