mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-18 18:38:05 +08:00
* wasm: do the async initialization only once This allows the rest of the app to access wasm synchronously. This allows removing of a global. * provide the wasm via vue provide/inject. There's still code directly accessing the wasm. That will be changed later. * MenuBarInput: use injected wasm instead of the global instance * Let the App handle event listeners * move stateful modules into state/ * state/fullscreen: create per instance * App: load the initial document list on mount. This got lost a few commits ago. Now it's back. * state/dialog: create per instance * util/input: remove dependency on global dialog instance * state/documents: create per instance * reponse-handler: move into EditorWasm * comingSoon: move into dialog * wasm: allow instantiating multiple editors * input handlers: do not look at canvases outside the mounted App * input: listen on the container instead of the window when possible * - removed proxy from wasm-loader - integrated with js-dispatcher - state functions to classes - integrated some upstream changes * fix errors caused by merge * Getting closer: - added global state to track all instances - fix fullscreen close trigger - wasm-loader is statefull - panic across instanes * - fix outline while using editor - removed circular import rule - added editorInstance to js message constructor * - changed input handler to a class - still need a better way of handeling it in App.vue * - fixed single instance of inputManager to weakmap * - fix no-explicit-any in a few places - removed global state from input.ts * simplified two long lines * removed global state * removed $data from App * add mut self to functions in api.rs * Update Workspace.vue remove outdated import * fixed missing import * Changes throughout code review; note this causes some bugs to be fixed in a later commit * PR review round 1 * - fix coming soon bugs - changed folder structure * moved declaration to .d.ts * - changed from classes to functions - moved decs back to app.vue * removed need to export js function to rust * changed folder structure * fixed indentation breaking multiline strings * Fix eslint rule to whitelist @/../ * Simplify strip-indents implementation * replace type assertions with better annotations or proper runtime checks * Small tweaks and code rearranging improvements after second code review pass * maybe fix mouse events * Add back preventDefault for mouse scroll * code review round 2 * Comment improvements * -removed runtime checks - fixed layers not showing * - extened proxy to cover classes - stopped multiple panics from logging - Stop wasm-bindgen from mut ref counting our struct * cleaned up messageConstructors exports * Fix input and fullscreen regressions Co-authored-by: Max Fisher <maxmfishernj@gmail.com> Co-authored-by: mfish33 <32677537+mfish33@users.noreply.github.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
/* eslint-disable func-names */
|
|
|
|
import { createJsDispatcher } from "@/dispatcher/js-dispatcher";
|
|
import { JsMessageType } from "@/dispatcher/js-messages";
|
|
|
|
export type WasmInstance = typeof import("@/../wasm/pkg");
|
|
export type RustEditorInstance = InstanceType<WasmInstance["Editor"]>;
|
|
|
|
let wasmImport: WasmInstance | null = null;
|
|
export async function initWasm() {
|
|
if (wasmImport !== null) return;
|
|
|
|
wasmImport = await import("@/../wasm/pkg").then(panicProxy);
|
|
}
|
|
|
|
// This works by proxying every function call wrapping a try-catch block to filter out redundant and confusing
|
|
// `RuntimeError: unreachable` exceptions sent to the console
|
|
function panicProxy<T extends object>(module: T): T {
|
|
const proxyHandler = {
|
|
get(target: T, propKey: string | symbol, receiver: unknown): unknown {
|
|
const targetValue = Reflect.get(target, propKey, receiver);
|
|
|
|
// Keep the original value being accessed if it isn't a function
|
|
const isFunction = typeof targetValue === "function";
|
|
if (!isFunction) return targetValue;
|
|
|
|
// Special handling to wrap the return of a constructor in the proxy
|
|
const isClass = isFunction && /^\s*class\s+/.test(targetValue.toString());
|
|
if (isClass) {
|
|
return function (...args: unknown[]) {
|
|
// eslint-disable-next-line new-cap
|
|
const result = new targetValue(...args);
|
|
return panicProxy(result);
|
|
};
|
|
}
|
|
|
|
// Replace the original function with a wrapper function that runs the original in a try-catch block
|
|
return function (...args: unknown[]) {
|
|
let result;
|
|
try {
|
|
// @ts-expect-error TypeScript does not know what `this` is, since it should be able to be anything
|
|
result = targetValue.apply(this, args);
|
|
} catch (err) {
|
|
// Suppress `unreachable` WebAssembly.RuntimeError exceptions
|
|
if (!`${err}`.startsWith("RuntimeError: unreachable")) throw err;
|
|
}
|
|
return result;
|
|
};
|
|
},
|
|
};
|
|
|
|
return new Proxy<T>(module, proxyHandler);
|
|
}
|
|
|
|
function getWasmInstance() {
|
|
if (wasmImport) return wasmImport;
|
|
throw new Error("Editor WASM backend was not initialized at application startup");
|
|
}
|
|
|
|
export function createEditorState() {
|
|
const dispatcher = createJsDispatcher();
|
|
const rawWasm = getWasmInstance();
|
|
|
|
const rustCallback = (messageType: JsMessageType, data: Record<string, unknown>) => {
|
|
dispatcher.handleJsMessage(messageType, data, rawWasm, instance);
|
|
};
|
|
|
|
const instance = new rawWasm.Editor(rustCallback);
|
|
|
|
return {
|
|
dispatcher,
|
|
rawWasm,
|
|
instance,
|
|
};
|
|
}
|
|
export type EditorState = Readonly<ReturnType<typeof createEditorState>>;
|