Skip to content

Commit e7f44ee

Browse files
committed
chore: move wasm loading state out of zustand
1 parent a045b5c commit e7f44ee

File tree

4 files changed

+87
-69
lines changed

4 files changed

+87
-69
lines changed

src/client/App.tsx

Lines changed: 20 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -31,35 +31,11 @@ import {
3131
} from "@/client/components/Tooltip";
3232
import { rpc } from "@/utils/rpc";
3333
import { useLoaderData, type LoaderFunctionArgs } from "react-router";
34-
import type {WorkspaceOwner} from "@/gen/types.ts";
35-
36-
type GoPreviewDef = (
37-
v: Record<string, string>,
38-
owner: WorkspaceOwner,
39-
params: Record<string, string>,
40-
) => Promise<string>;
41-
42-
// Extend the Window object to include the Go related code that is added from
43-
// wasm_exec.js and our loaded Go code.
44-
declare global {
45-
interface Window {
46-
// Loaded from wasm
47-
go_preview?: GoPreviewDef;
48-
Go: { new (): Go };
49-
CODE?: string;
50-
}
51-
}
52-
53-
declare class Go {
54-
argv: string[];
55-
env: { [envKey: string]: string };
56-
exit: (code: number) => void;
57-
importObject: WebAssembly.Imports;
58-
exited: boolean;
59-
mem: DataView;
60-
run(instance: WebAssembly.Instance): Promise<void>;
61-
}
34+
import { initWasm, type WasmLoadState } from "@/utils/wasm";
6235

36+
/**
37+
* Load the shared code if present.
38+
*/
6339
export const loader = async ({ params }: LoaderFunctionArgs) => {
6440
const { id } = params;
6541
if (!id) {
@@ -79,8 +55,12 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
7955
};
8056

8157
export const App = () => {
82-
const $wasmState = useStore((state) => state.wasmState);
83-
const $setWasmState = useStore((state) => state.setWasmState);
58+
const [wasmLoadState, setWasmLoadingState] = useState<WasmLoadState>(() => {
59+
if (window.go_preview) {
60+
return "loaded";
61+
}
62+
return "loading";
63+
});
8464
const $setCode = useStore((store) => store.setCode);
8565
const code = useLoaderData<typeof loader>();
8666

@@ -92,30 +72,15 @@ export const App = () => {
9272
$setCode(code);
9373
}, [code, $setCode]);
9474

95-
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
9675
useEffect(() => {
97-
const initWasm = async () => {
98-
try {
99-
const goWasm = new window.Go();
100-
const result = await WebAssembly.instantiateStreaming(
101-
fetch(
102-
import.meta.env.PROD
103-
? "/assets/build/preview.wasm"
104-
: "/build/preview.wasm",
105-
),
106-
goWasm.importObject,
107-
);
108-
109-
goWasm.run(result.instance);
110-
$setWasmState("loaded");
111-
} catch (e) {
112-
$setWasmState("error");
113-
console.error(e);
114-
}
115-
};
116-
117-
if ($wasmState !== "loaded") {
118-
initWasm();
76+
if (!window.go_preview) {
77+
initWasm().then((loadState) => {
78+
setWasmLoadingState(loadState);
79+
});
80+
} else {
81+
// We assume that if `window.go_preview` has already created then the wasm
82+
// has already been initiated.
83+
setWasmLoadingState("loaded");
11984
}
12085
}, []);
12186

@@ -163,14 +128,14 @@ export const App = () => {
163128
</div>
164129
</nav>
165130

166-
<ResizablePanelGroup aria-hidden={!$wasmState} direction={"horizontal"}>
131+
<ResizablePanelGroup direction={"horizontal"}>
167132
{/* EDITOR */}
168133
<Editor />
169134

170135
<ResizableHandle className="bg-surface-quaternary" />
171136

172137
{/* PREVIEW */}
173-
<Preview />
138+
<Preview wasmLoadState={wasmLoadState} />
174139
</ResizablePanelGroup>
175140
</main>
176141
);

src/client/Preview.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ import {
5656
} from "@/client/components/Select";
5757
import { mockUsers } from "@/owner";
5858
import { checkerModule } from "./snippets";
59+
import type { WasmLoadState } from "@/utils/wasm";
5960

60-
export const Preview: FC = () => {
61-
const $wasmState = useStore((state) => state.wasmState);
61+
type PreviewProps = {
62+
wasmLoadState: WasmLoadState;
63+
}
64+
65+
export const Preview: FC<PreviewProps> = ({ wasmLoadState }) => {
6266
const $code = useStore((state) => state.code);
6367
const $errors = useStore((state) => state.errors);
6468
const $setError = useStore((state) => state.setError);
@@ -99,7 +103,7 @@ export const Preview: FC = () => {
99103
}, [output]);
100104

101105
useEffect(() => {
102-
if ($wasmState === "loading" || !window.go_preview) {
106+
if (wasmLoadState === "loading" || !window.go_preview) {
103107
return;
104108
}
105109

@@ -151,7 +155,7 @@ export const Preview: FC = () => {
151155
};
152156

153157
getOutput();
154-
}, [debouncedCode, $setError, $wasmState, $setParameters, $form, $owner]);
158+
}, [debouncedCode, $setError, wasmLoadState, $setParameters, $form, $owner]);
155159

156160
return (
157161
<Tabs.Root
@@ -161,9 +165,9 @@ export const Preview: FC = () => {
161165
onValueChange={(tab) => setTab(() => tab)}
162166
>
163167
<ResizablePanel className="relative flex h-full max-h-full flex-col">
164-
{$wasmState !== "loaded" ? (
168+
{wasmLoadState !== "loaded" ? (
165169
<div className="absolute top-0 left-0 z-10 flex h-full w-full items-center justify-center backdrop-blur-sm">
166-
{$wasmState === "loading" ? <WasmLoading /> : <WasmError />}
170+
{wasmLoadState === "loading" ? <WasmLoading /> : <WasmError />}
167171
</div>
168172
) : null}
169173

@@ -205,12 +209,12 @@ export const Preview: FC = () => {
205209
<Tabs.Content value="preview" asChild={true}>
206210
<div
207211
aria-hidden={
208-
$wasmState !== "loaded" ||
212+
wasmLoadState !== "loaded" ||
209213
($errors.show && $errors.diagnostics.length > 0)
210214
}
211215
className={cn(
212216
"flex h-full w-full flex-col items-start gap-4 p-5 ",
213-
($wasmState !== "loaded" ||
217+
(wasmLoadState !== "loaded" ||
214218
($errors.show && $errors.diagnostics.length > 0)) &&
215219
"pointer-events-none",
216220
isDebug && "max-h-[calc(100%-48px)]",
@@ -224,7 +228,7 @@ export const Preview: FC = () => {
224228
</p>
225229

226230
<AnimatePresence>
227-
{isDebouncing && $wasmState === "loaded" ? (
231+
{isDebouncing && wasmLoadState === "loaded" ? (
228232
<motion.div
229233
initial={{ opacity: 0, scale: 0.75 }}
230234
animate={{ opacity: 1, scale: 1 }}

src/client/store.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import { mockUsers } from "@/owner";
77

88
export type FormState = Record<string, string>;
99

10-
type WasmState = "loaded" | "loading" | "error";
11-
1210
type ErrorsState = {
1311
diagnostics: Diagnostic[];
1412
show: boolean;
@@ -24,13 +22,11 @@ type State = {
2422
editor: editor.IStandaloneCodeEditor | null;
2523
parameters: ParameterWithSource[];
2624
form: FormState;
27-
wasmState: WasmState;
2825
owner: WorkspaceOwner;
2926
errors: ErrorsState;
3027
setCode: (code: string) => void;
3128
setError: (diagnostics: Diagnostic[]) => void;
3229
toggleShowError: (open?: boolean) => void;
33-
setWasmState: (wasmState: WasmState) => void;
3430
setParameters: (parameters: ParameterWithSource[]) => void;
3531
setFormState: (key: string, value: string) => void;
3632
setEditor: (editor: editor.IStandaloneCodeEditor) => void;
@@ -65,7 +61,6 @@ export const useStore = create<State>()((set) => ({
6561
},
6662
};
6763
}),
68-
setWasmState: (wasmState) => set((_) => ({ wasmState })),
6964
setParameters: (parameters) => set((_) => ({ parameters })),
7065
setFormState: (key, value) =>
7166
set((state) => {

src/utils/wasm.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type { WorkspaceOwner } from "@/gen/types";
2+
3+
export type WasmLoadState = "loaded" | "loading" | "error";
4+
5+
type GoPreviewDef = (
6+
/**
7+
* A virtual filetree
8+
*/
9+
files: Record<string, string>,
10+
owner: WorkspaceOwner,
11+
params: Record<string, string>,
12+
) => Promise<string>;
13+
14+
// Extend the Window object to include the Go related code that is added from
15+
// wasm_exec.js and our loaded Go code.
16+
declare global {
17+
interface Window {
18+
// Loaded from wasm
19+
go_preview?: GoPreviewDef;
20+
Go: { new (): Go };
21+
CODE?: string;
22+
}
23+
}
24+
25+
declare class Go {
26+
argv: string[];
27+
env: { [envKey: string]: string };
28+
exit: (code: number) => void;
29+
importObject: WebAssembly.Imports;
30+
exited: boolean;
31+
mem: DataView;
32+
run(instance: WebAssembly.Instance): Promise<void>;
33+
}
34+
35+
export const initWasm = async (): Promise<WasmLoadState> => {
36+
try {
37+
const goWasm = new window.Go();
38+
const result = await WebAssembly.instantiateStreaming(
39+
fetch(
40+
import.meta.env.PROD
41+
? "/assets/build/preview.wasm"
42+
: "/build/preview.wasm",
43+
),
44+
goWasm.importObject,
45+
);
46+
47+
goWasm.run(result.instance);
48+
49+
return "loaded";
50+
} catch (e) {
51+
console.error(e);
52+
return "error";
53+
}
54+
};

0 commit comments

Comments
 (0)