Skip to content

Commit a28efb7

Browse files
committed
feat: add performance monitoring w/ opt-in
1 parent 0bb0146 commit a28efb7

11 files changed

Lines changed: 414 additions & 291 deletions

.github/workflows/pages-build.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ jobs:
5757
env:
5858
CI: true
5959
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60+
FARO_SOURCEMAP_UPLOAD_API_KEY: ${{secrets.FARO_SOURCEMAP_UPLOAD_API_KEY}}
6061

6162
run: |
6263
bun i @minecraftmetascript/mms-wasm@latest

bun.lock

Lines changed: 99 additions & 183 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
"@codemirror/lsp-client": "^6.1.2",
4545
"@codemirror/search": "^6.5.11",
4646
"@codemirror/view": "^6.38.1",
47+
"@grafana/faro-rollup-plugin": "^0.6.0",
48+
"@grafana/faro-web-sdk": "^1.19.0",
49+
"@grafana/faro-web-tracing": "^1.19.0",
4750
"@minecraftmetascript/mms-wasm": "../mms/result/js",
4851
"@open-rpc/client-js": "^1.8.1",
4952
"@steeze-ui/svelte-icon": "^1.6.2",

src/lib/MMSProject.svelte.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import MMSWasm from '@minecraftmetascript/mms-wasm/dist/main.wasm?init';
33
import * as deepslate from 'deepslate';
44
import * as zip from '@zip.js/zip.js';
55
import { debounce } from 'es-toolkit';
6+
import { registerEvent } from './observability';
67

78
export class MMSFile {
89
private _content: string;
@@ -121,6 +122,7 @@ export class MMSProject {
121122
globalThis.mmsLspRead = this.lspRead;
122123
const wasmInstance = await MMSWasm(this.goInstance.importObject);
123124
this.goInstance.run(wasmInstance).catch((err) => {
125+
registerEvent("MMS_WASM: Init Failed", { err })
124126
console.error('MMS WASM Failed: ', { err });
125127
});
126128
signal?.addEventListener('abort', () => {

src/lib/PerformanceConsent.svelte

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<script lang="ts">
2+
import { Icon } from '@steeze-ui/svelte-icon';
3+
import { Check, X } from '@steeze-ui/tabler-icons';
4+
import { onMount } from 'svelte';
5+
import { slide } from 'svelte/transition';
6+
import { initializeObservability } from './observability';
7+
8+
const initConsent = async () => {
9+
await initializeObservability();
10+
shouldShow = false;
11+
};
12+
13+
const acceptConsent = () => {
14+
localStorage.setItem('MMS:telemetry_consent', 'true');
15+
initConsent();
16+
};
17+
18+
const declineConsent = () => {
19+
localStorage.setItem('MMS:telemetry_consent', 'false');
20+
shouldShow = false;
21+
};
22+
23+
let shouldShow = $state(false);
24+
let timeout = $state(0);
25+
const TIMEOUT_MAX = 30000;
26+
let animateIdx = 0; // prevent multiple running when
27+
onMount(() => {
28+
const consent = localStorage.getItem('MMS:telemetry_consent');
29+
if (!consent) {
30+
shouldShow = true;
31+
timeout = TIMEOUT_MAX;
32+
let current = performance.now();
33+
const idx = ++animateIdx;
34+
const animate = () => {
35+
if (animateIdx !== idx) return;
36+
const elapsed = performance.now() - current;
37+
timeout -= elapsed;
38+
timeout = Math.max(timeout, 0);
39+
current = performance.now();
40+
if (timeout > 0) {
41+
requestAnimationFrame(animate);
42+
} else {
43+
console.log('Done?');
44+
}
45+
};
46+
animate();
47+
} else if (consent.toLowerCase() === 'true') {
48+
initConsent().catch(console.error);
49+
}
50+
});
51+
</script>
52+
53+
{#if shouldShow}
54+
<div
55+
class="flex max-h-svh w-svw justify-between bg-slate-300 px-32 py-4"
56+
transition:slide={{ duration: 500, delay: 500 }}
57+
>
58+
<p>
59+
This website can leverage
60+
<a href="https://grafana.com/products/cloud/frontend-observability/" target="_blank"
61+
>Grafana Frontend Observability</a
62+
>
63+
to gather performance and error insights.
64+
<br />
65+
<strong
66+
>Your privacy is important to us, and this will NOT be activated unless you consent.</strong
67+
>
68+
</p>
69+
70+
<div class="flex min-h-0 items-center gap-4">
71+
<button
72+
class="relative flex h-12 items-center gap-2 bg-green-400 px-4 py-1 text-lg font-bold text-green-950 hover:bg-green-600"
73+
onclick={() => acceptConsent()}
74+
>
75+
<Icon src={Check} class="size-6 stroke-4" />
76+
Accept
77+
<div class="absolute bottom-0 left-0 h-1 w-full bg-green-300">
78+
<div
79+
class="h-full bg-green-900"
80+
style:width="{100 - (timeout / TIMEOUT_MAX) * 100}%"
81+
></div>
82+
</div>
83+
</button>
84+
<button
85+
class="flex h-12 items-center gap-2 bg-red-400 px-4 py-1 text-lg text-red-950 hover:bg-red-600"
86+
onclick={() => declineConsent()}
87+
>
88+
<div class="relative">
89+
<Icon src={X} class="absolute top-1/2 left-1/2 h-4 w-4 -translate-1/2" />
90+
</div>
91+
Decline
92+
</button>
93+
</div>
94+
</div>
95+
{/if}

src/lib/deepslate/DeepslateRendererOffloaded.svelte

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
} from '@steeze-ui/tabler-icons';
2828
import { pixelToCoordinate } from './lib';
2929
import { base } from '$app/paths';
30+
import { registerError, registerEvent } from '../observability';
3031
3132
const editor = useEditorContext();
3233
@@ -125,6 +126,8 @@
125126
if (mousePos && editor.previewSymbol) fetchValue();
126127
});
127128
129+
const editor = useEditorContext();
130+
128131
worker.addEventListener('message', (e) => {
129132
const message = parse(DeepslateRenderWorkerMessageSchema, e.data);
130133
switch (message.kind) {
@@ -133,6 +136,15 @@
133136
label: message.label,
134137
value: typeof message.value === 'number' ? message.value.toFixed(2) : message.value
135138
};
139+
break;
140+
case 'error':
141+
const e =
142+
message.error instanceof Error
143+
? message.error
144+
: new Error('Error in deepslate worker', { cause: message.error });
145+
let content = editor.project.source['wasm.mms'];
146+
registerError(e, content);
147+
break;
136148
}
137149
});
138150

src/lib/deepslate/proto.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export const InjestNoiseMessageSchema = v.object({
5656
value: v.union([v.looseObject({}), v.string()])
5757
});
5858

59+
export const ErrorCallback = v.object({
60+
kind: v.literal('error'),
61+
error: v.any()
62+
});
63+
5964
export const DeepslateRenderWorkerMessageSchema = v.union([
6065
CanvasInitMessageSchema,
6166
UpdatePreviewMessageSchema,
@@ -64,6 +69,7 @@ export const DeepslateRenderWorkerMessageSchema = v.union([
6469
InjestNoiseMessageSchema,
6570
RequestValueAtPoint,
6671
ResponseValueAtPoint,
67-
UpdateMarkerPosSchema
72+
UpdateMarkerPosSchema,
73+
ErrorCallback
6874
]);
6975
export type DeepslateRenderWorkerMessage = v.InferOutput<typeof DeepslateRenderWorkerMessageSchema>;

0 commit comments

Comments
 (0)