@motion-core/motion-gpu 0.6.0 → 0.8.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 +40 -1
- package/dist/core/pointer.d.ts +96 -0
- package/dist/core/pointer.d.ts.map +1 -0
- package/dist/core/pointer.js +71 -0
- package/dist/core/pointer.js.map +1 -0
- package/dist/motion-gpu.css +295 -0
- package/dist/react/advanced.js +2 -1
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -1
- package/dist/react/use-pointer.d.ts +94 -0
- package/dist/react/use-pointer.d.ts.map +1 -0
- package/dist/react/use-pointer.js +285 -0
- package/dist/react/use-pointer.js.map +1 -0
- package/dist/svelte/advanced.js +2 -1
- package/dist/svelte/index.d.ts +2 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +2 -1
- package/dist/svelte/use-pointer.d.ts +94 -0
- package/dist/svelte/use-pointer.d.ts.map +1 -0
- package/dist/svelte/use-pointer.js +292 -0
- package/dist/svelte/use-pointer.js.map +1 -0
- package/dist/vue/FragCanvas.js +8 -0
- package/dist/vue/FragCanvas.js.map +1 -0
- package/dist/vue/FragCanvas.vue.d.ts +49 -0
- package/dist/vue/FragCanvas.vue.d.ts.map +1 -0
- package/dist/vue/FragCanvas.vue_vue_type_script_setup_true_lang.js +228 -0
- package/dist/vue/FragCanvas.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.js +8 -0
- package/dist/vue/MotionGPUErrorOverlay.js.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.vue.d.ts +8 -0
- package/dist/vue/MotionGPUErrorOverlay.vue.d.ts.map +1 -0
- package/dist/vue/MotionGPUErrorOverlay.vue_vue_type_script_setup_true_lang.js +166 -0
- package/dist/vue/MotionGPUErrorOverlay.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/Portal.js +7 -0
- package/dist/vue/Portal.js.map +1 -0
- package/dist/vue/Portal.vue.d.ts +18 -0
- package/dist/vue/Portal.vue.d.ts.map +1 -0
- package/dist/vue/Portal.vue_vue_type_script_setup_true_lang.js +29 -0
- package/dist/vue/Portal.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/vue/advanced.d.ts +12 -0
- package/dist/vue/advanced.d.ts.map +1 -0
- package/dist/vue/advanced.js +15 -0
- package/dist/vue/frame-context.d.ts +22 -0
- package/dist/vue/frame-context.d.ts.map +1 -0
- package/dist/vue/frame-context.js +38 -0
- package/dist/vue/frame-context.js.map +1 -0
- package/dist/vue/index.d.ts +21 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +14 -0
- package/dist/vue/motiongpu-context.d.ts +81 -0
- package/dist/vue/motiongpu-context.d.ts.map +1 -0
- package/dist/vue/motiongpu-context.js +29 -0
- package/dist/vue/motiongpu-context.js.map +1 -0
- package/dist/vue/shims-vue.d.js +0 -0
- package/dist/vue/use-motiongpu-user-context.d.ts +44 -0
- package/dist/vue/use-motiongpu-user-context.d.ts.map +1 -0
- package/dist/vue/use-motiongpu-user-context.js +76 -0
- package/dist/vue/use-motiongpu-user-context.js.map +1 -0
- package/dist/vue/use-pointer.d.ts +94 -0
- package/dist/vue/use-pointer.d.ts.map +1 -0
- package/dist/vue/use-pointer.js +298 -0
- package/dist/vue/use-pointer.js.map +1 -0
- package/dist/vue/use-texture.d.ts +45 -0
- package/dist/vue/use-texture.d.ts.map +1 -0
- package/dist/vue/use-texture.js +135 -0
- package/dist/vue/use-texture.js.map +1 -0
- package/package.json +25 -7
- package/src/lib/core/pointer.ts +177 -0
- package/src/lib/react/index.ts +10 -0
- package/src/lib/react/use-pointer.ts +515 -0
- package/src/lib/svelte/index.ts +10 -0
- package/src/lib/svelte/use-pointer.ts +507 -0
- package/src/lib/vue/FragCanvas.vue +294 -0
- package/src/lib/vue/MotionGPUErrorOverlay.vue +518 -0
- package/src/lib/vue/Portal.vue +46 -0
- package/src/lib/vue/advanced.ts +32 -0
- package/src/lib/vue/frame-context.ts +96 -0
- package/src/lib/vue/index.ts +78 -0
- package/src/lib/vue/motiongpu-context.ts +97 -0
- package/src/lib/vue/shims-vue.d.ts +6 -0
- package/src/lib/vue/use-motiongpu-user-context.ts +145 -0
- package/src/lib/vue/use-pointer.ts +514 -0
- package/src/lib/vue/use-texture.ts +232 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import type { MotionGPUErrorReport } from '../core/error-report.js';
|
|
4
|
+
import Portal from './Portal.vue';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
report: MotionGPUErrorReport;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const props = defineProps<Props>();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Normalizes a string for case-insensitive comparison.
|
|
14
|
+
*/
|
|
15
|
+
function normalizeErrorText(value: string): string {
|
|
16
|
+
return value
|
|
17
|
+
.trim()
|
|
18
|
+
.replace(/[.:!]+$/g, '')
|
|
19
|
+
.toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolves a non-redundant message to display, stripping a duplicated title prefix.
|
|
24
|
+
*/
|
|
25
|
+
function resolveDisplayMessage(value: MotionGPUErrorReport): string {
|
|
26
|
+
const rawMessage = value.message.trim();
|
|
27
|
+
if (rawMessage.length === 0) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const normalizedMessage = normalizeErrorText(rawMessage);
|
|
32
|
+
const normalizedTitle = normalizeErrorText(value.title);
|
|
33
|
+
if (normalizedMessage === normalizedTitle) {
|
|
34
|
+
return '';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const escapedTitle = value.title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
38
|
+
const prefixPattern = new RegExp(`^${escapedTitle}\\s*[:\\-|]\\s*`, 'i');
|
|
39
|
+
const stripped = rawMessage.replace(prefixPattern, '').trim();
|
|
40
|
+
return stripped.length > 0 ? stripped : rawMessage;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Formats the runtime context payload as a human-readable YAML-ish block.
|
|
45
|
+
*/
|
|
46
|
+
function formatRuntimeContext(context: MotionGPUErrorReport['context']): string {
|
|
47
|
+
if (!context) {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const indentBlock = (value: string, spaces = 2): string => {
|
|
52
|
+
const prefix = ' '.repeat(spaces);
|
|
53
|
+
return value
|
|
54
|
+
.split('\n')
|
|
55
|
+
.map((line) => `${prefix}${line}`)
|
|
56
|
+
.join('\n');
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const formatMaterialSignature = (value: string): string => {
|
|
60
|
+
const trimmed = value.trim();
|
|
61
|
+
if (trimmed.length === 0) {
|
|
62
|
+
return '<empty>';
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
66
|
+
} catch {
|
|
67
|
+
return trimmed;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const lines: string[] = [];
|
|
72
|
+
if (context.materialSignature) {
|
|
73
|
+
lines.push('materialSignature:');
|
|
74
|
+
lines.push(indentBlock(formatMaterialSignature(context.materialSignature)));
|
|
75
|
+
}
|
|
76
|
+
if (context.passGraph) {
|
|
77
|
+
lines.push('passGraph:');
|
|
78
|
+
lines.push(` passCount: ${context.passGraph.passCount}`);
|
|
79
|
+
lines.push(` enabledPassCount: ${context.passGraph.enabledPassCount}`);
|
|
80
|
+
lines.push(' inputs:');
|
|
81
|
+
if (context.passGraph.inputs.length === 0) {
|
|
82
|
+
lines.push(' - <none>');
|
|
83
|
+
} else {
|
|
84
|
+
for (const input of context.passGraph.inputs) {
|
|
85
|
+
lines.push(` - ${input}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
lines.push(' outputs:');
|
|
89
|
+
if (context.passGraph.outputs.length === 0) {
|
|
90
|
+
lines.push(' - <none>');
|
|
91
|
+
} else {
|
|
92
|
+
for (const output of context.passGraph.outputs) {
|
|
93
|
+
lines.push(` - ${output}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
lines.push('activeRenderTargets:');
|
|
98
|
+
if (context.activeRenderTargets.length === 0) {
|
|
99
|
+
lines.push(' - <none>');
|
|
100
|
+
} else {
|
|
101
|
+
for (const target of context.activeRenderTargets) {
|
|
102
|
+
lines.push(` - ${target}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return lines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const displayMessage = computed(() => resolveDisplayMessage(props.report));
|
|
109
|
+
const showDisplayMessage = computed(() => displayMessage.value.length > 0);
|
|
110
|
+
const detailsText = computed(() => props.report.details.join('\n'));
|
|
111
|
+
const stackText = computed(() => props.report.stack.join('\n'));
|
|
112
|
+
const runtimeContextText = computed(() => formatRuntimeContext(props.report.context));
|
|
113
|
+
const detailsSummary = computed(() =>
|
|
114
|
+
props.report.source ? 'Additional diagnostics' : 'Technical details'
|
|
115
|
+
);
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<template>
|
|
119
|
+
<Portal>
|
|
120
|
+
<div class="motiongpu-error-overlay" role="presentation">
|
|
121
|
+
<section
|
|
122
|
+
class="motiongpu-error-dialog"
|
|
123
|
+
role="alertdialog"
|
|
124
|
+
aria-live="assertive"
|
|
125
|
+
aria-modal="true"
|
|
126
|
+
data-testid="motiongpu-error"
|
|
127
|
+
>
|
|
128
|
+
<header class="motiongpu-error-header">
|
|
129
|
+
<div class="motiongpu-error-header-top">
|
|
130
|
+
<div class="motiongpu-error-badges">
|
|
131
|
+
<div class="motiongpu-error-badge-wrap">
|
|
132
|
+
<p class="motiongpu-error-badge motiongpu-error-badge-phase">
|
|
133
|
+
{{ report.phase }}
|
|
134
|
+
</p>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="motiongpu-error-badge-wrap">
|
|
137
|
+
<p class="motiongpu-error-badge motiongpu-error-badge-severity">
|
|
138
|
+
{{ report.severity }}
|
|
139
|
+
</p>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<h2 class="motiongpu-error-title">{{ report.title }}</h2>
|
|
144
|
+
<p class="motiongpu-error-recoverable">
|
|
145
|
+
Recoverable: <span>{{ report.recoverable ? 'yes' : 'no' }}</span>
|
|
146
|
+
</p>
|
|
147
|
+
</header>
|
|
148
|
+
<div class="motiongpu-error-body">
|
|
149
|
+
<p v-if="showDisplayMessage" class="motiongpu-error-message">
|
|
150
|
+
{{ displayMessage }}
|
|
151
|
+
</p>
|
|
152
|
+
<p class="motiongpu-error-hint">{{ report.hint }}</p>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<section v-if="report.source" class="motiongpu-error-source" aria-label="Source">
|
|
156
|
+
<h3 class="motiongpu-error-source-title">Source</h3>
|
|
157
|
+
<div class="motiongpu-error-source-frame" role="presentation">
|
|
158
|
+
<div class="motiongpu-error-source-tabs" role="tablist" aria-label="Source files">
|
|
159
|
+
<span
|
|
160
|
+
class="motiongpu-error-source-tab motiongpu-error-source-tab-active"
|
|
161
|
+
role="tab"
|
|
162
|
+
aria-selected="true"
|
|
163
|
+
>{{ report.source.location
|
|
164
|
+
}}<template v-if="report.source.column"
|
|
165
|
+
>, col {{ report.source.column }}</template
|
|
166
|
+
></span
|
|
167
|
+
>
|
|
168
|
+
<span class="motiongpu-error-source-tab-spacer" aria-hidden="true"></span>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<div class="motiongpu-error-source-snippet">
|
|
172
|
+
<div
|
|
173
|
+
v-for="snippetLine in report.source.snippet"
|
|
174
|
+
:key="`snippet-${snippetLine.number}`"
|
|
175
|
+
class="motiongpu-error-source-row"
|
|
176
|
+
:class="{
|
|
177
|
+
'motiongpu-error-source-row-active': snippetLine.highlight
|
|
178
|
+
}"
|
|
179
|
+
>
|
|
180
|
+
<span class="motiongpu-error-source-line">{{ snippetLine.number }}</span>
|
|
181
|
+
<span class="motiongpu-error-source-code">{{ snippetLine.code || ' ' }}</span>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</section>
|
|
186
|
+
|
|
187
|
+
<div class="motiongpu-error-sections">
|
|
188
|
+
<details v-if="report.details.length > 0" class="motiongpu-error-details" open>
|
|
189
|
+
<summary>{{ detailsSummary }}</summary>
|
|
190
|
+
<pre>{{ detailsText }}</pre>
|
|
191
|
+
</details>
|
|
192
|
+
<details v-if="report.stack.length > 0" class="motiongpu-error-details">
|
|
193
|
+
<summary>Stack trace</summary>
|
|
194
|
+
<pre>{{ stackText }}</pre>
|
|
195
|
+
</details>
|
|
196
|
+
<details v-if="report.context" class="motiongpu-error-details">
|
|
197
|
+
<summary>Runtime context</summary>
|
|
198
|
+
<pre>{{ runtimeContextText }}</pre>
|
|
199
|
+
</details>
|
|
200
|
+
</div>
|
|
201
|
+
</section>
|
|
202
|
+
</div>
|
|
203
|
+
</Portal>
|
|
204
|
+
</template>
|
|
205
|
+
|
|
206
|
+
<style>
|
|
207
|
+
.motiongpu-error-overlay {
|
|
208
|
+
--motiongpu-base-hue: var(--base-hue, 265);
|
|
209
|
+
--motiongpu-color-background: oklch(0.2178 0.0056 var(--motiongpu-base-hue));
|
|
210
|
+
--motiongpu-color-background-muted: oklch(0.261 0.007 var(--motiongpu-base-hue));
|
|
211
|
+
--motiongpu-color-foreground: oklch(1 0 0);
|
|
212
|
+
--motiongpu-color-foreground-muted: oklch(0.6699 0.0081 var(--motiongpu-base-hue));
|
|
213
|
+
--motiongpu-color-card: var(--motiongpu-color-background);
|
|
214
|
+
--motiongpu-color-accent: oklch(0.6996 0.181959 44.4414);
|
|
215
|
+
--motiongpu-color-accent-secondary: oklch(0.5096 0.131959 44.4414);
|
|
216
|
+
--motiongpu-color-border: oklch(0.928 0.013 var(--motiongpu-base-hue) / 0.05);
|
|
217
|
+
--motiongpu-color-white-fixed: oklch(1 0 0);
|
|
218
|
+
--motiongpu-shadow-card: var(
|
|
219
|
+
--shadow-2xl,
|
|
220
|
+
0px 1px 1px -0.5px rgba(0, 0, 0, 0.06),
|
|
221
|
+
0px 3px 3px -1.5px rgba(0, 0, 0, 0.06),
|
|
222
|
+
0px 6px 6px -3px rgba(0, 0, 0, 0.06),
|
|
223
|
+
0px 12px 12px -6px rgba(0, 0, 0, 0.06),
|
|
224
|
+
0px 24px 24px -12px rgba(0, 0, 0, 0.05),
|
|
225
|
+
0px 48px 48px -24px rgba(0, 0, 0, 0.06)
|
|
226
|
+
);
|
|
227
|
+
--motiongpu-radius-md: var(--radius-md, 0.5rem);
|
|
228
|
+
--motiongpu-radius-lg: var(--radius-lg, 0.75rem);
|
|
229
|
+
--motiongpu-radius-xl: var(--radius-xl, 1rem);
|
|
230
|
+
--motiongpu-font-sans: var(--font-sans, 'Inter', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif);
|
|
231
|
+
--motiongpu-font-mono: var(--font-mono, 'SFMono-Regular', 'Menlo', 'Consolas', monospace);
|
|
232
|
+
position: fixed;
|
|
233
|
+
inset: 0;
|
|
234
|
+
display: grid;
|
|
235
|
+
place-items: center;
|
|
236
|
+
padding: clamp(0.75rem, 1.4vw, 1.5rem);
|
|
237
|
+
background: rgba(0, 0, 0, 0.8);
|
|
238
|
+
backdrop-filter: blur(10px);
|
|
239
|
+
z-index: 2147483647;
|
|
240
|
+
font-family: var(--motiongpu-font-sans);
|
|
241
|
+
color-scheme: dark;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.motiongpu-error-dialog {
|
|
245
|
+
width: min(52rem, calc(100vw - 1.5rem));
|
|
246
|
+
max-height: min(84vh, 44rem);
|
|
247
|
+
overflow: auto;
|
|
248
|
+
margin: 0;
|
|
249
|
+
padding: 1.1rem;
|
|
250
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
251
|
+
border-radius: var(--motiongpu-radius-xl);
|
|
252
|
+
max-width: calc(100vw - 1.5rem);
|
|
253
|
+
box-sizing: border-box;
|
|
254
|
+
font-size: 0.875rem;
|
|
255
|
+
font-weight: 400;
|
|
256
|
+
line-height: 1.45;
|
|
257
|
+
background: var(--motiongpu-color-card);
|
|
258
|
+
color: var(--motiongpu-color-foreground);
|
|
259
|
+
box-shadow: var(--motiongpu-shadow-card);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.motiongpu-error-header {
|
|
263
|
+
display: grid;
|
|
264
|
+
gap: 0.55rem;
|
|
265
|
+
padding-bottom: 0.9rem;
|
|
266
|
+
border-bottom: 1px solid var(--motiongpu-color-border);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.motiongpu-error-header-top {
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: flex-start;
|
|
272
|
+
gap: 0.75rem;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.motiongpu-error-badges {
|
|
276
|
+
display: inline-flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
gap: 0.4rem;
|
|
279
|
+
flex-wrap: wrap;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.motiongpu-error-badge-wrap {
|
|
283
|
+
display: inline-flex;
|
|
284
|
+
align-items: center;
|
|
285
|
+
gap: 0.4rem;
|
|
286
|
+
width: fit-content;
|
|
287
|
+
padding: 0.18rem;
|
|
288
|
+
border-radius: 999px;
|
|
289
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
290
|
+
background: var(--motiongpu-color-background-muted);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.motiongpu-error-badge {
|
|
294
|
+
display: inline-flex;
|
|
295
|
+
align-items: center;
|
|
296
|
+
margin: 0;
|
|
297
|
+
padding: 0.22rem 0.56rem;
|
|
298
|
+
border-radius: 999px;
|
|
299
|
+
font-size: 0.66rem;
|
|
300
|
+
letter-spacing: 0.08em;
|
|
301
|
+
line-height: 1;
|
|
302
|
+
font-weight: 500;
|
|
303
|
+
text-transform: uppercase;
|
|
304
|
+
color: var(--motiongpu-color-white-fixed);
|
|
305
|
+
background: linear-gradient(
|
|
306
|
+
180deg,
|
|
307
|
+
var(--motiongpu-color-accent) 0%,
|
|
308
|
+
var(--motiongpu-color-accent-secondary) 100%
|
|
309
|
+
);
|
|
310
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.24);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.motiongpu-error-recoverable {
|
|
314
|
+
margin: 0;
|
|
315
|
+
font-size: 0.67rem;
|
|
316
|
+
line-height: 1.2;
|
|
317
|
+
letter-spacing: 0.06em;
|
|
318
|
+
text-transform: uppercase;
|
|
319
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.motiongpu-error-recoverable span {
|
|
323
|
+
font-family: var(--motiongpu-font-mono);
|
|
324
|
+
color: var(--motiongpu-color-foreground);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.motiongpu-error-title {
|
|
328
|
+
margin: 0;
|
|
329
|
+
font-size: clamp(1.02rem, 1vw + 0.72rem, 1.32rem);
|
|
330
|
+
font-weight: 500;
|
|
331
|
+
line-height: 1.18;
|
|
332
|
+
letter-spacing: -0.02em;
|
|
333
|
+
text-wrap: balance;
|
|
334
|
+
color: var(--motiongpu-color-foreground);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.motiongpu-error-body {
|
|
338
|
+
display: grid;
|
|
339
|
+
gap: 0.62rem;
|
|
340
|
+
margin-top: 0.92rem;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.motiongpu-error-message {
|
|
344
|
+
margin: 0;
|
|
345
|
+
padding: 0.72rem 0.78rem;
|
|
346
|
+
border: 1px solid color-mix(in oklch, var(--motiongpu-color-accent) 28%, transparent);
|
|
347
|
+
border-radius: var(--motiongpu-radius-md);
|
|
348
|
+
background: color-mix(in oklch, var(--motiongpu-color-accent) 10%, transparent);
|
|
349
|
+
font-size: 0.82rem;
|
|
350
|
+
line-height: 1.4;
|
|
351
|
+
font-weight: 400;
|
|
352
|
+
color: var(--motiongpu-color-foreground);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.motiongpu-error-hint {
|
|
356
|
+
margin: 0;
|
|
357
|
+
font-size: 0.82rem;
|
|
358
|
+
line-height: 1.45;
|
|
359
|
+
font-weight: 400;
|
|
360
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.motiongpu-error-sections {
|
|
364
|
+
display: grid;
|
|
365
|
+
gap: 0.62rem;
|
|
366
|
+
margin-top: 0.95rem;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.motiongpu-error-source {
|
|
370
|
+
display: grid;
|
|
371
|
+
gap: 0.48rem;
|
|
372
|
+
margin-top: 0.96rem;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.motiongpu-error-source-title {
|
|
376
|
+
margin: 0;
|
|
377
|
+
font-size: 0.8rem;
|
|
378
|
+
font-weight: 500;
|
|
379
|
+
line-height: 1.3;
|
|
380
|
+
letter-spacing: 0.045em;
|
|
381
|
+
text-transform: uppercase;
|
|
382
|
+
color: var(--motiongpu-color-foreground);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.motiongpu-error-source-frame {
|
|
386
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
387
|
+
border-radius: var(--motiongpu-radius-lg);
|
|
388
|
+
overflow: hidden;
|
|
389
|
+
background: var(--motiongpu-color-background-muted);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.motiongpu-error-source-tabs {
|
|
393
|
+
display: flex;
|
|
394
|
+
align-items: stretch;
|
|
395
|
+
border-bottom: 1px solid var(--motiongpu-color-border);
|
|
396
|
+
background: var(--motiongpu-color-background);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.motiongpu-error-source-tab {
|
|
400
|
+
display: inline-flex;
|
|
401
|
+
align-items: center;
|
|
402
|
+
padding: 0.5rem 0.68rem;
|
|
403
|
+
font-size: 0.76rem;
|
|
404
|
+
font-weight: 400;
|
|
405
|
+
line-height: 1.2;
|
|
406
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
407
|
+
border-right: 1px solid var(--motiongpu-color-border);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.motiongpu-error-source-tab-active {
|
|
411
|
+
color: var(--motiongpu-color-foreground);
|
|
412
|
+
background: var(--motiongpu-color-background-muted);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.motiongpu-error-source-tab-spacer {
|
|
416
|
+
flex: 1 1 auto;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.motiongpu-error-source-snippet {
|
|
420
|
+
display: grid;
|
|
421
|
+
background: var(--motiongpu-color-background-muted);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.motiongpu-error-source-row {
|
|
425
|
+
display: grid;
|
|
426
|
+
grid-template-columns: 2rem minmax(0, 1fr);
|
|
427
|
+
align-items: start;
|
|
428
|
+
gap: 0.42rem;
|
|
429
|
+
padding: 0.2rem 0.68rem;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.motiongpu-error-source-row-active {
|
|
433
|
+
background: color-mix(in oklch, var(--motiongpu-color-accent) 10%, transparent);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.motiongpu-error-source-line {
|
|
437
|
+
font-family: var(--motiongpu-font-mono);
|
|
438
|
+
font-size: 0.77rem;
|
|
439
|
+
font-weight: 400;
|
|
440
|
+
line-height: 1.3;
|
|
441
|
+
font-variant-numeric: tabular-nums;
|
|
442
|
+
font-feature-settings: 'tnum' 1;
|
|
443
|
+
border-right: 1px solid var(--motiongpu-color-border);
|
|
444
|
+
color: var(--motiongpu-color-foreground-muted);
|
|
445
|
+
text-align: left;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.motiongpu-error-source-code {
|
|
449
|
+
font-family: var(--motiongpu-font-mono);
|
|
450
|
+
font-size: 0.77rem;
|
|
451
|
+
font-weight: 400;
|
|
452
|
+
line-height: 1.3;
|
|
453
|
+
color: var(--motiongpu-color-foreground);
|
|
454
|
+
white-space: pre-wrap;
|
|
455
|
+
word-break: break-word;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.motiongpu-error-details {
|
|
459
|
+
border: 1px solid var(--motiongpu-color-border);
|
|
460
|
+
border-radius: var(--motiongpu-radius-lg);
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
background: var(--motiongpu-color-background);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.motiongpu-error-details summary {
|
|
466
|
+
cursor: pointer;
|
|
467
|
+
padding: 0.56rem 0.68rem;
|
|
468
|
+
font-size: 0.7rem;
|
|
469
|
+
letter-spacing: 0.07em;
|
|
470
|
+
line-height: 1.2;
|
|
471
|
+
font-weight: 500;
|
|
472
|
+
text-transform: uppercase;
|
|
473
|
+
color: var(--motiongpu-color-foreground);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.motiongpu-error-details[open] summary {
|
|
477
|
+
border-bottom: 1px solid var(--motiongpu-color-border);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.motiongpu-error-details pre {
|
|
481
|
+
margin: 0;
|
|
482
|
+
padding: 0.62rem 0.68rem;
|
|
483
|
+
white-space: pre-wrap;
|
|
484
|
+
word-break: break-word;
|
|
485
|
+
overflow: auto;
|
|
486
|
+
background: var(--motiongpu-color-background-muted);
|
|
487
|
+
font-size: 0.74rem;
|
|
488
|
+
line-height: 1.4;
|
|
489
|
+
font-weight: 400;
|
|
490
|
+
color: var(--motiongpu-color-foreground);
|
|
491
|
+
font-family: var(--motiongpu-font-mono);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
@media (max-width: 42rem) {
|
|
495
|
+
.motiongpu-error-overlay {
|
|
496
|
+
padding: 0.62rem;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.motiongpu-error-dialog {
|
|
500
|
+
padding: 0.85rem;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.motiongpu-error-title {
|
|
504
|
+
font-size: 1.02rem;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.motiongpu-error-header-top {
|
|
508
|
+
flex-direction: column;
|
|
509
|
+
align-items: flex-start;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
@media (prefers-reduced-motion: reduce) {
|
|
514
|
+
.motiongpu-error-overlay {
|
|
515
|
+
backdrop-filter: none;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
target?: string | HTMLElement | null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
9
|
+
target: 'body'
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
defineSlots<{
|
|
13
|
+
default(): unknown;
|
|
14
|
+
}>();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolves a teleport target to a concrete DOM element, falling back to body.
|
|
18
|
+
*/
|
|
19
|
+
function resolveTargetElement(
|
|
20
|
+
input: string | HTMLElement | null | undefined
|
|
21
|
+
): HTMLElement | string {
|
|
22
|
+
if (typeof input === 'string') {
|
|
23
|
+
if (typeof document === 'undefined') {
|
|
24
|
+
return input;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return document.querySelector<HTMLElement>(input) ?? document.body;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (input) {
|
|
31
|
+
return input;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return typeof document === 'undefined' ? 'body' : document.body;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const teleportTarget = computed(() => resolveTargetElement(props.target));
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<Teleport :to="teleportTarget">
|
|
42
|
+
<div class="motiongpu-portal-root">
|
|
43
|
+
<slot />
|
|
44
|
+
</div>
|
|
45
|
+
</Teleport>
|
|
46
|
+
</template>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue adapter advanced entrypoint for MotionGPU.
|
|
3
|
+
*/
|
|
4
|
+
export * from './index.js';
|
|
5
|
+
export { applySchedulerPreset, captureSchedulerDebugSnapshot } from '../core/scheduler-helpers.js';
|
|
6
|
+
export { setMotionGPUUserContext, useMotionGPUUserContext } from './use-motiongpu-user-context.js';
|
|
7
|
+
export type {
|
|
8
|
+
ApplySchedulerPresetOptions,
|
|
9
|
+
SchedulerDebugSnapshot,
|
|
10
|
+
SchedulerPreset,
|
|
11
|
+
SchedulerPresetConfig
|
|
12
|
+
} from '../core/scheduler-helpers.js';
|
|
13
|
+
export type { MotionGPUUserContext, MotionGPUUserNamespace } from './motiongpu-context.js';
|
|
14
|
+
export type {
|
|
15
|
+
FrameProfilingSnapshot,
|
|
16
|
+
FrameKey,
|
|
17
|
+
FrameTaskInvalidation,
|
|
18
|
+
FrameTaskInvalidationToken,
|
|
19
|
+
FrameRunTimings,
|
|
20
|
+
FrameScheduleSnapshot,
|
|
21
|
+
FrameStage,
|
|
22
|
+
FrameStageCallback,
|
|
23
|
+
FrameTimingStats,
|
|
24
|
+
FrameTask
|
|
25
|
+
} from '../core/frame-registry.js';
|
|
26
|
+
export type { SetMotionGPUUserContextOptions } from './use-motiongpu-user-context.js';
|
|
27
|
+
export type {
|
|
28
|
+
RenderPassContext,
|
|
29
|
+
RenderTarget,
|
|
30
|
+
UniformLayout,
|
|
31
|
+
UniformLayoutEntry
|
|
32
|
+
} from '../core/types.js';
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getCurrentInstance, inject, onBeforeUnmount, provide, type InjectionKey } from 'vue';
|
|
2
|
+
import {
|
|
3
|
+
createFrameRegistry,
|
|
4
|
+
type FrameCallback,
|
|
5
|
+
type FrameKey,
|
|
6
|
+
type FrameProfilingSnapshot,
|
|
7
|
+
type FrameRegistry,
|
|
8
|
+
type FrameRunTimings,
|
|
9
|
+
type FrameScheduleSnapshot,
|
|
10
|
+
type FrameStage,
|
|
11
|
+
type FrameStageCallback,
|
|
12
|
+
type FrameTask,
|
|
13
|
+
type FrameTaskInvalidation,
|
|
14
|
+
type FrameTaskInvalidationToken,
|
|
15
|
+
type UseFrameOptions,
|
|
16
|
+
type UseFrameResult
|
|
17
|
+
} from '../core/frame-registry.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Vue injection key used to expose the active frame registry.
|
|
21
|
+
*/
|
|
22
|
+
export const frameRegistryKey: InjectionKey<FrameRegistry> = Symbol('motiongpu.frame-registry');
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
createFrameRegistry,
|
|
26
|
+
type FrameCallback,
|
|
27
|
+
type FrameKey,
|
|
28
|
+
type FrameProfilingSnapshot,
|
|
29
|
+
type FrameRegistry,
|
|
30
|
+
type FrameRunTimings,
|
|
31
|
+
type FrameScheduleSnapshot,
|
|
32
|
+
type FrameStage,
|
|
33
|
+
type FrameStageCallback,
|
|
34
|
+
type FrameTask,
|
|
35
|
+
type FrameTaskInvalidation,
|
|
36
|
+
type FrameTaskInvalidationToken,
|
|
37
|
+
type UseFrameOptions,
|
|
38
|
+
type UseFrameResult
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Provides a frame registry through Vue provide/inject.
|
|
43
|
+
*
|
|
44
|
+
* @param registry - Frame registry instance to provide to descendants.
|
|
45
|
+
*/
|
|
46
|
+
export function provideFrameRegistry(registry: FrameRegistry): void {
|
|
47
|
+
provide(frameRegistryKey, registry);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Registers a frame callback using an auto-generated task key.
|
|
52
|
+
*/
|
|
53
|
+
export function useFrame(callback: FrameCallback, options?: UseFrameOptions): UseFrameResult;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Registers a frame callback with an explicit task key.
|
|
57
|
+
*/
|
|
58
|
+
export function useFrame(
|
|
59
|
+
key: FrameKey,
|
|
60
|
+
callback: FrameCallback,
|
|
61
|
+
options?: UseFrameOptions
|
|
62
|
+
): UseFrameResult;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Registers a callback in the active frame registry and auto-unsubscribes
|
|
66
|
+
* when the owning component is unmounted.
|
|
67
|
+
*
|
|
68
|
+
* @throws {Error} When called outside `<FragCanvas>`.
|
|
69
|
+
* @throws {Error} When the callback is missing.
|
|
70
|
+
*/
|
|
71
|
+
export function useFrame(
|
|
72
|
+
keyOrCallback: FrameKey | FrameCallback,
|
|
73
|
+
callbackOrOptions?: FrameCallback | UseFrameOptions,
|
|
74
|
+
maybeOptions?: UseFrameOptions
|
|
75
|
+
): UseFrameResult {
|
|
76
|
+
const registry = inject(frameRegistryKey, null);
|
|
77
|
+
if (!registry) {
|
|
78
|
+
throw new Error('useFrame must be used inside <FragCanvas>');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const registration =
|
|
82
|
+
typeof keyOrCallback === 'function'
|
|
83
|
+
? registry.register(keyOrCallback, callbackOrOptions as UseFrameOptions | undefined)
|
|
84
|
+
: registry.register(keyOrCallback, callbackOrOptions as FrameCallback, maybeOptions);
|
|
85
|
+
|
|
86
|
+
if (getCurrentInstance()) {
|
|
87
|
+
onBeforeUnmount(registration.unsubscribe);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
task: registration.task,
|
|
92
|
+
start: registration.start,
|
|
93
|
+
stop: registration.stop,
|
|
94
|
+
started: registration.started
|
|
95
|
+
};
|
|
96
|
+
}
|