@liquid-glass-forge/vue 0.0.0-stage → 0.1.0-rc.2
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 +21 -0
- package/NOTICE +7 -0
- package/README.md +45 -2
- package/dist/index.d.ts +40 -0
- package/dist/index.js +101 -0
- package/package.json +37 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nami Poursafargholi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Liquid Glass Forge
|
|
2
|
+
Copyright (c) 2026 Nami Poursafargholi. MIT licensed.
|
|
3
|
+
|
|
4
|
+
Independent implementation inspired by Apple's Liquid Glass design.
|
|
5
|
+
Not affiliated with, sponsored by or endorsed by Apple Inc.
|
|
6
|
+
Apple and its design/assets remain subject to their respective rights.
|
|
7
|
+
No Apple assets or upstream Liquid Glass/Sileo implementation is included.
|
package/README.md
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# <img src="https://raw.githubusercontent.com/namipsg/liquid-glass-forge/main/showcase/public/frameworks/vue.svg" width="20" height="20" alt=""> @liquid-glass-forge/vue
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Integrate Forge scenes and glass surfaces into Vue.
|
|
4
|
+
|
|
5
|
+
[Live showcase](https://namipsg.github.io/liquid-glass-forge/?framework=vue#developers) · [Examples](https://namipsg.github.io/liquid-glass-forge/examples.html) · [Documentation](https://github.com/namipsg/liquid-glass-forge/blob/main/docs/PHASE-8.md) · [Support](https://namipsg.github.io/liquid-glass-forge/support.html)
|
|
6
|
+
|
|
7
|
+
## Install in Vue
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @liquid-glass-forge/vue@next @liquid-glass-forge/dom@next
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The starter uses CSS glass and expects Vue 3.5.44. Keep the Forge packages at matching versions.
|
|
14
|
+
The [live installer](https://namipsg.github.io/liquid-glass-forge/?framework=vue#developers)
|
|
15
|
+
shows verified exact versions and has a copy button.
|
|
16
|
+
|
|
17
|
+
[Open the Vue starter](https://github.com/namipsg/liquid-glass-forge/blob/main/docs/QUICKSTART.md#vue) · [Add WebGL refraction](https://github.com/namipsg/liquid-glass-forge/blob/main/docs/QUICKSTART.md#add-webgl-refraction)
|
|
18
|
+
|
|
19
|
+
[](https://namipsg.github.io/liquid-glass-forge/?framework=vue#developers)
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Experimental preview `0.1.0-rc.2`. The pinned Vue peer is **3.5.44**.
|
|
24
|
+
Vue remains external; the package ships ESM and declarations without bundling it.
|
|
25
|
+
|
|
26
|
+
Import `GlassSurface`, `GlassSceneProvider` and `useGlassSurface` from
|
|
27
|
+
`@liquid-glass-forge/vue`. Import `@liquid-glass-forge/dom/styles.css` in the
|
|
28
|
+
application for server and no-JavaScript material. JavaScript injects no stylesheet.
|
|
29
|
+
|
|
30
|
+
`GlassSurface` renders a div, a decorative canvas and ordinary default-slot
|
|
31
|
+
content. Native HTML attributes/events are forwarded to the div. A component ref
|
|
32
|
+
exposes `element`, `target` and `invalidate` (see `GlassSurfaceHandle`). Without a
|
|
33
|
+
provider the surface uses CSS/opaque material.
|
|
34
|
+
|
|
35
|
+
A provider accepts a `source`/`renderer` pair, `paused`, `timeoutMs` and `onError`.
|
|
36
|
+
It owns one shared scene and borrows reusable source/renderer objects. Mount and
|
|
37
|
+
activation start/resume it; deactivation pauses it; unmount disposes it. Source or
|
|
38
|
+
renderer identity changes replace the scene. Inactive changes wait for activation.
|
|
39
|
+
|
|
40
|
+
`useGlassSurface({material})` accepts a material value, ref or getter and returns
|
|
41
|
+
`element`, `target` shallow refs plus `invalidate`. Render the target as a dedicated
|
|
42
|
+
direct canvas child with decorative class/attributes. Vue owns both nodes. Do not
|
|
43
|
+
control its backing dimensions or draw into its context while attached.
|
|
44
|
+
|
|
45
|
+
See the [integration guide](https://github.com/namipsg/liquid-glass-forge/blob/main/docs/PHASE-8.md) for ownership, KeepAlive, exact Vite/Nuxt consumer
|
|
46
|
+
verification and remaining limits.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { HTMLAttributes, MaybeRefOrGetter, PropType, ShallowRef } from 'vue';
|
|
2
|
+
import type { GlassMaterial, GlassRenderer, SceneSource } from '@liquid-glass-forge/dom';
|
|
3
|
+
/** Caller owns source/renderer descriptors. This provider owns engine lifetimes. */
|
|
4
|
+
export declare const GlassSceneProvider: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
5
|
+
source: PropType<SceneSource>;
|
|
6
|
+
renderer: PropType<GlassRenderer>;
|
|
7
|
+
timeoutMs: NumberConstructor;
|
|
8
|
+
paused: BooleanConstructor;
|
|
9
|
+
onError: PropType<(error: unknown) => void>;
|
|
10
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
13
|
+
source: PropType<SceneSource>;
|
|
14
|
+
renderer: PropType<GlassRenderer>;
|
|
15
|
+
timeoutMs: NumberConstructor;
|
|
16
|
+
paused: BooleanConstructor;
|
|
17
|
+
onError: PropType<(error: unknown) => void>;
|
|
18
|
+
}>> & Readonly<{}>, {
|
|
19
|
+
paused: boolean;
|
|
20
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
21
|
+
export interface UseGlassSurfaceOptions {
|
|
22
|
+
readonly material?: MaybeRefOrGetter<GlassMaterial>;
|
|
23
|
+
}
|
|
24
|
+
export interface GlassSurfaceBinding {
|
|
25
|
+
readonly element: ShallowRef<HTMLElement | null>;
|
|
26
|
+
readonly target: ShallowRef<HTMLCanvasElement | null>;
|
|
27
|
+
readonly invalidate: () => void;
|
|
28
|
+
}
|
|
29
|
+
/** Setup-only composable. Supply a dedicated direct canvas child owned by Vue. */
|
|
30
|
+
export declare function useGlassSurface(options?: UseGlassSurfaceOptions): GlassSurfaceBinding;
|
|
31
|
+
export interface GlassSurfaceHandle {
|
|
32
|
+
readonly element: HTMLElement | null;
|
|
33
|
+
readonly target: HTMLCanvasElement | null;
|
|
34
|
+
readonly invalidate: () => void;
|
|
35
|
+
}
|
|
36
|
+
export interface GlassSurfaceProps extends HTMLAttributes {
|
|
37
|
+
readonly material?: GlassMaterial;
|
|
38
|
+
}
|
|
39
|
+
/** Stable server/client markup. Native attributes/events and slots remain Vue-owned. */
|
|
40
|
+
export declare const GlassSurface: import("vue").DefineSetupFnComponent<GlassSurfaceProps, {}, {}, GlassSurfaceProps & {}, import("vue").PublicProps>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { defineComponent, h, inject, mergeProps, onActivated, onBeforeUnmount, onDeactivated, onMounted, provide, shallowRef, toRaw, toValue, watch } from 'vue';
|
|
2
|
+
import { createGlassScene } from '@liquid-glass-forge/dom';
|
|
3
|
+
const SceneKey = Symbol('LiquidGlassForgeScene');
|
|
4
|
+
/** Caller owns source/renderer descriptors. This provider owns engine lifetimes. */
|
|
5
|
+
export const GlassSceneProvider = defineComponent({
|
|
6
|
+
name: 'GlassSceneProvider',
|
|
7
|
+
props: {
|
|
8
|
+
source: Object,
|
|
9
|
+
renderer: Object,
|
|
10
|
+
timeoutMs: Number,
|
|
11
|
+
paused: Boolean,
|
|
12
|
+
onError: Function,
|
|
13
|
+
},
|
|
14
|
+
setup(props, { slots }) {
|
|
15
|
+
const scene = shallowRef(null);
|
|
16
|
+
provide(SceneKey, { scene });
|
|
17
|
+
let mounted = false, active = false, dirty = true;
|
|
18
|
+
const retire = () => { const old = scene.value; scene.value = null; old?.dispose(); };
|
|
19
|
+
const start = () => {
|
|
20
|
+
if (!mounted || !active)
|
|
21
|
+
return;
|
|
22
|
+
if (dirty || !scene.value) {
|
|
23
|
+
retire();
|
|
24
|
+
const next = createGlassScene({
|
|
25
|
+
...(props.source ? { source: toRaw(props.source) } : {}),
|
|
26
|
+
...(props.renderer ? { renderer: toRaw(props.renderer) } : {}),
|
|
27
|
+
...(props.timeoutMs === undefined ? {} : { timeoutMs: props.timeoutMs }),
|
|
28
|
+
onError: cause => props.onError?.(cause),
|
|
29
|
+
});
|
|
30
|
+
if (props.paused)
|
|
31
|
+
next.pause();
|
|
32
|
+
scene.value = next;
|
|
33
|
+
dirty = false;
|
|
34
|
+
}
|
|
35
|
+
else if (!props.paused && scene.value.state === 'paused')
|
|
36
|
+
scene.value.resume();
|
|
37
|
+
};
|
|
38
|
+
watch(() => [props.source, props.renderer, props.timeoutMs], () => { dirty = true; retire(); start(); }, { flush: 'post' });
|
|
39
|
+
watch(() => props.paused, paused => { if (paused)
|
|
40
|
+
scene.value?.pause();
|
|
41
|
+
else
|
|
42
|
+
start(); }, { flush: 'post' });
|
|
43
|
+
onMounted(() => { mounted = active = true; start(); });
|
|
44
|
+
onActivated(() => { active = true; start(); });
|
|
45
|
+
onDeactivated(() => { active = false; scene.value?.pause(); });
|
|
46
|
+
onBeforeUnmount(() => { mounted = active = false; retire(); });
|
|
47
|
+
return () => slots.default?.();
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
/** Setup-only composable. Supply a dedicated direct canvas child owned by Vue. */
|
|
51
|
+
export function useGlassSurface(options = {}) {
|
|
52
|
+
const shared = inject(SceneKey, null);
|
|
53
|
+
const element = shallowRef(null), target = shallowRef(null);
|
|
54
|
+
let boundElement = null, boundTarget = null, boundScene = null;
|
|
55
|
+
let active = false, attachment = null, owned = null;
|
|
56
|
+
const material = () => options.material === undefined ? 'glass' : toValue(options.material);
|
|
57
|
+
const detach = () => { attachment?.dispose(); attachment = null; boundElement = boundTarget = null; boundScene = null; };
|
|
58
|
+
const attach = () => {
|
|
59
|
+
if (!active || !element.value || !target.value) {
|
|
60
|
+
detach();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!shared && !owned)
|
|
64
|
+
owned = createGlassScene();
|
|
65
|
+
const scene = shared ? shared.scene.value : owned;
|
|
66
|
+
if (!scene || scene.state === 'destroyed') {
|
|
67
|
+
detach();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (attachment && boundScene === scene && boundElement === element.value && boundTarget === target.value)
|
|
71
|
+
return;
|
|
72
|
+
detach();
|
|
73
|
+
if (owned?.state === 'paused')
|
|
74
|
+
owned.resume();
|
|
75
|
+
attachment = scene.attach(element.value, { target: target.value, material: material() });
|
|
76
|
+
boundElement = element.value;
|
|
77
|
+
boundTarget = target.value;
|
|
78
|
+
boundScene = scene;
|
|
79
|
+
};
|
|
80
|
+
watch([element, target, () => shared?.scene.value], attach, { flush: 'post' });
|
|
81
|
+
watch(material, value => attachment?.update({ material: value }), { flush: 'post' });
|
|
82
|
+
onMounted(() => { active = true; attach(); });
|
|
83
|
+
onActivated(() => { if (active)
|
|
84
|
+
return; active = true; attach(); });
|
|
85
|
+
onDeactivated(() => { active = false; detach(); owned?.pause(); });
|
|
86
|
+
onBeforeUnmount(() => { active = false; detach(); owned?.dispose(); owned = null; });
|
|
87
|
+
return { element, target, invalidate: () => attachment?.invalidate() };
|
|
88
|
+
}
|
|
89
|
+
/** Stable server/client markup. Native attributes/events and slots remain Vue-owned. */
|
|
90
|
+
export const GlassSurface = defineComponent((props, { attrs, slots, expose }) => {
|
|
91
|
+
const material = () => props.material ?? 'glass';
|
|
92
|
+
const binding = useGlassSurface({ material });
|
|
93
|
+
expose({ get element() { return binding.element.value; }, get target() { return binding.target.value; }, invalidate: binding.invalidate });
|
|
94
|
+
return () => h('div', mergeProps(attrs, { class: 'lgf-host', ref: binding.element }), [
|
|
95
|
+
h('canvas', { class: 'lgf-surface', 'data-lgf-material': material(), 'aria-hidden': 'true', role: 'presentation', tabindex: -1, ref: binding.target }),
|
|
96
|
+
...(slots.default?.() ?? []),
|
|
97
|
+
]);
|
|
98
|
+
}, {
|
|
99
|
+
name: 'GlassSurface', inheritAttrs: false,
|
|
100
|
+
props: { material: { type: String, default: 'glass' } },
|
|
101
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liquid-glass-forge/vue",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0-rc.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20.19.0"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"NOTICE",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@liquid-glass-forge/dom": "0.1.0-rc.2"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"vue": "3.5.44"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://namipsg.github.io/liquid-glass-forge/?framework=vue#developers",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/namipsg/liquid-glass-forge.git",
|
|
34
|
+
"directory": "packages/vue"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
}
|
|
39
|
+
}
|