|
1 | 1 | <template> |
2 | 2 | <MainWindow /> |
3 | 3 |
|
4 | | - <div class="unsupported-modal-backdrop" v-if="showUnsupportedModal"> |
| 4 | + <div class="unsupported-modal-backdrop" v-if="apiUnsupported" ref="unsupported"> |
5 | 5 | <LayoutCol class="unsupported-modal"> |
6 | 6 | <h2>Your browser currently doesn't support Graphite</h2> |
7 | 7 | <p>Unfortunately, some features won't work properly. Please upgrade to a modern browser such as Firefox, Chrome, Edge, or Safari version 15 or later.</p> |
|
11 | 11 | API which is required for using the editor. However, you can still explore the user interface. |
12 | 12 | </p> |
13 | 13 | <LayoutRow> |
14 | | - <button class="unsupported-modal-button" @click="() => closeModal()">I understand, let's just see the interface</button> |
| 14 | + <button class="unsupported-modal-button" @click="() => closeUnsupportedWarning()">I understand, let's just see the interface</button> |
15 | 15 | </LayoutRow> |
16 | 16 | </LayoutCol> |
17 | 17 | </div> |
@@ -258,78 +258,96 @@ img { |
258 | 258 | <script lang="ts"> |
259 | 259 | import { defineComponent } from "vue"; |
260 | 260 |
|
261 | | -import { createAutoSaveManager } from "@/lifetime/auto-save"; |
262 | | -import { initErrorHandling } from "@/lifetime/errors"; |
263 | | -import { createInputManager, InputManager } from "@/lifetime/input"; |
264 | | -import { createDialogState, DialogState } from "@/state/dialog"; |
265 | | -import { createFullscreenState, FullscreenState } from "@/state/fullscreen"; |
266 | | -import { createPortfolioState, PortfolioState } from "@/state/portfolio"; |
267 | | -import { createEditorState, EditorState } from "@/state/wasm-loader"; |
268 | | -import { createWorkspaceState, WorkspaceState } from "@/state/workspace"; |
| 261 | +import { createBuildMetadataManager } from "@/io-managers/build-metadata"; |
| 262 | +import { createClipboardManager } from "@/io-managers/clipboard"; |
| 263 | +import { createHyperlinkManager } from "@/io-managers/hyperlinks"; |
| 264 | +import { createInputManager } from "@/io-managers/input"; |
| 265 | +import { createPanicManager } from "@/io-managers/panic"; |
| 266 | +import { createPersistenceManager } from "@/io-managers/persistence"; |
| 267 | +import { createDialogState, DialogState } from "@/state-providers/dialog"; |
| 268 | +import { createFontsState, FontsState } from "@/state-providers/fonts"; |
| 269 | +import { createFullscreenState, FullscreenState } from "@/state-providers/fullscreen"; |
| 270 | +import { createPortfolioState, PortfolioState } from "@/state-providers/portfolio"; |
| 271 | +import { createWorkspaceState, WorkspaceState } from "@/state-providers/workspace"; |
| 272 | +import { createEditor, Editor } from "@/wasm-communication/editor"; |
269 | 273 |
|
270 | 274 | import LayoutCol from "@/components/layout/LayoutCol.vue"; |
271 | 275 | import LayoutRow from "@/components/layout/LayoutRow.vue"; |
272 | 276 | import MainWindow from "@/components/window/MainWindow.vue"; |
273 | 277 |
|
274 | | -// Vue injects don't play well with TypeScript, and all injects will show up as `any`. As a workaround, we can define these types. |
| 278 | +const managerDestructors: { |
| 279 | + createBuildMetadataManager?: () => void; |
| 280 | + createClipboardManager?: () => void; |
| 281 | + createHyperlinkManager?: () => void; |
| 282 | + createInputManager?: () => void; |
| 283 | + createPanicManager?: () => void; |
| 284 | + createPersistenceManager?: () => void; |
| 285 | +} = {}; |
| 286 | +
|
| 287 | +// Vue injects don't play well with TypeScript (all injects will show up as `any`) but we can define these types as a solution |
275 | 288 | declare module "@vue/runtime-core" { |
| 289 | + // Systems `provide`d by the root App to be `inject`ed into descendant components and used for reactive bindings |
276 | 290 | interface ComponentCustomProperties { |
| 291 | + // Graphite WASM editor instance |
| 292 | + editor: Editor; |
| 293 | +
|
| 294 | + // State provider systems |
277 | 295 | dialog: DialogState; |
| 296 | + fonts: FontsState; |
| 297 | + fullscreen: FullscreenState; |
278 | 298 | portfolio: PortfolioState; |
279 | 299 | workspace: WorkspaceState; |
280 | | - fullscreen: FullscreenState; |
281 | | - editor: EditorState; |
282 | | - // This must be set to optional because there is a time in the lifecycle of the component where inputManager is undefined. |
283 | | - // That's because we initialize inputManager in `mounted()` rather than `data()` since the div hasn't been created yet. |
284 | | - inputManager?: InputManager; |
285 | 300 | } |
286 | 301 | } |
287 | 302 |
|
288 | 303 | export default defineComponent({ |
289 | 304 | provide() { |
290 | | - return { |
291 | | - editor: this.editor, |
292 | | - dialog: this.dialog, |
293 | | - portfolio: this.portfolio, |
294 | | - workspace: this.workspace, |
295 | | - fullscreen: this.fullscreen, |
296 | | - inputManager: this.inputManager, |
297 | | - }; |
| 305 | + return { ...this.$data }; |
298 | 306 | }, |
299 | 307 | data() { |
300 | | - // Initialize the Graphite WASM editor instance |
301 | | - const editor = createEditorState(); |
302 | | -
|
303 | | - // Initialize other stateful Vue systems |
304 | | - const dialog = createDialogState(editor); |
305 | | - const portfolio = createPortfolioState(editor); |
306 | | - const workspace = createWorkspaceState(editor); |
307 | | - const fullscreen = createFullscreenState(); |
308 | | - initErrorHandling(editor, dialog); |
309 | | - createAutoSaveManager(editor, portfolio); |
310 | | -
|
| 308 | + const editor = createEditor(); |
311 | 309 | return { |
| 310 | + // Graphite WASM editor instance |
312 | 311 | editor, |
313 | | - dialog, |
314 | | - portfolio, |
315 | | - workspace, |
316 | | - fullscreen, |
317 | | - showUnsupportedModal: !("BigInt64Array" in window), |
318 | | - inputManager: undefined as undefined | InputManager, |
| 312 | +
|
| 313 | + // State provider systems |
| 314 | + dialog: createDialogState(editor), |
| 315 | + fonts: createFontsState(editor), |
| 316 | + fullscreen: createFullscreenState(), |
| 317 | + portfolio: createPortfolioState(editor), |
| 318 | + workspace: createWorkspaceState(editor), |
319 | 319 | }; |
320 | 320 | }, |
| 321 | + computed: { |
| 322 | + apiUnsupported() { |
| 323 | + return !("BigInt64Array" in window); |
| 324 | + }, |
| 325 | + }, |
321 | 326 | methods: { |
322 | | - closeModal() { |
323 | | - this.showUnsupportedModal = false; |
| 327 | + closeUnsupportedWarning() { |
| 328 | + const element = this.$refs.unsupported as HTMLElement; |
| 329 | + element.parentElement?.removeChild(element); |
324 | 330 | }, |
325 | 331 | }, |
326 | | - mounted() { |
327 | | - this.inputManager = createInputManager(this.editor, this.$el.parentElement, this.dialog, this.portfolio, this.fullscreen); |
328 | | -
|
| 332 | + async mounted() { |
| 333 | + // Initialize managers, which are isolated systems that subscribe to backend messages to link them to browser API functionality (like JS events, IndexedDB, etc.) |
| 334 | + Object.assign(managerDestructors, { |
| 335 | + createBuildMetadataManager: createBuildMetadataManager(this.editor), |
| 336 | + createClipboardManager: createClipboardManager(this.editor), |
| 337 | + createHyperlinkManager: createHyperlinkManager(this.editor), |
| 338 | + createInputManager: createInputManager(this.editor, this.$el.parentElement, this.dialog, this.portfolio, this.fullscreen), |
| 339 | + createPanicManager: createPanicManager(this.editor, this.dialog), |
| 340 | + createPersistenceManager: await createPersistenceManager(this.editor, this.portfolio), |
| 341 | + }); |
| 342 | +
|
| 343 | + // Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready |
329 | 344 | this.editor.instance.init_app(); |
330 | 345 | }, |
331 | 346 | beforeUnmount() { |
332 | | - this.inputManager?.removeListeners(); |
| 347 | + // Call the destructor for each manager |
| 348 | + Object.values(managerDestructors).forEach((destructor) => destructor?.()); |
| 349 | +
|
| 350 | + // Destroy the WASM editor instance |
333 | 351 | this.editor.instance.free(); |
334 | 352 | }, |
335 | 353 | components: { |
|
0 commit comments