Merge branch 'main' into franknoirot/4088/decouple-homeMachine
This commit is contained in:
		
							
								
								
									
										68
									
								
								.github/workflows/build-test-publish-apps.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								.github/workflows/build-test-publish-apps.yml
									
									
									
									
										vendored
									
									
								
							| @ -51,6 +51,8 @@ jobs: | ||||
|         run: | | ||||
|           VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons | ||||
|  | ||||
|       # TODO: see if we need to inject updater nightly URL here https://dl.zoo.dev/releases/modeling-app/nightly/last_update.json | ||||
|  | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|         with: | ||||
|           name: prepared-files | ||||
| @ -61,25 +63,12 @@ jobs: | ||||
|       - id: export_version | ||||
|         run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT" | ||||
|  | ||||
|       - name: Prepare electron-builder.yml file for nightly | ||||
|         if: ${{ github.event_name == 'schedule' }} | ||||
|         run: | | ||||
|           yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' electron-builder.yml | ||||
|  | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|         if: ${{ github.event_name == 'schedule' }} | ||||
|         with: | ||||
|           name: prepared-files-nightly | ||||
|           path: | | ||||
|             electron-builder.yml | ||||
|  | ||||
|       - name: Prepare electron-builder.yml file for updater test | ||||
|         if: ${{ env.CUT_RELEASE_PR == 'true' }} | ||||
|         run: | | ||||
|           yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test"' electron-builder.yml | ||||
|  | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|         if: ${{ env.CUT_RELEASE_PR == 'true' }} | ||||
|         with: | ||||
|           name: prepared-files-updater-test | ||||
|           path: | | ||||
| @ -119,16 +108,6 @@ jobs: | ||||
|           mkdir src/wasm-lib/pkg | ||||
|           cp prepared-files/src/wasm-lib/pkg/wasm_lib* src/wasm-lib/pkg | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
|         if: ${{ github.event_name == 'schedule' }} | ||||
|         name: prepared-files-nightly | ||||
|  | ||||
|       - name: Copy updated electron-builder.yml file for nightly build | ||||
|         if: ${{ github.event_name == 'schedule' }} | ||||
|         run: | | ||||
|           ls -R prepared-files-nightly | ||||
|           cp prepared-files-nightly/electron-builder.yml electron-builder.yml | ||||
|  | ||||
|       - name: Sync node version and setup cache | ||||
|         uses: actions/setup-node@v4 | ||||
|         with: | ||||
| @ -173,17 +152,11 @@ jobs: | ||||
|  | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|         with: | ||||
|           name: out-arm64-${{ matrix.os }} | ||||
|           name: out-${{ matrix.os }} | ||||
|           path: | | ||||
|             out/Zoo*arm64*.* | ||||
|             out/Zoo*.* | ||||
|             out/latest*.yml | ||||
|  | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|         with: | ||||
|           name: out-x64-${{ matrix.os }} | ||||
|           path: | | ||||
|             out/Zoo*x*64*.* | ||||
|  | ||||
|       # TODO: add the 'Build for Mac TestFlight (nightly)' stage back | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
| @ -203,16 +176,10 @@ jobs: | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|         if: ${{ env.CUT_RELEASE_PR == 'true' }} | ||||
|         with: | ||||
|           name: updater-test-arm64-${{ matrix.os }} | ||||
|           name: updater-test-${{ matrix.os }} | ||||
|           path: | | ||||
|             out/Zoo*arm64*.* | ||||
|  | ||||
|       - uses: actions/upload-artifact@v3 | ||||
|         if: ${{ env.CUT_RELEASE_PR == 'true' }} | ||||
|         with: | ||||
|           name: updater-test-x64-${{ matrix.os }} | ||||
|           path: | | ||||
|             out/Zoo*x64*.* | ||||
|             out/Zoo*.* | ||||
|             out/latest*.yml | ||||
|  | ||||
|  | ||||
|   publish-apps-release: | ||||
| @ -234,32 +201,17 @@ jobs: | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
|         with: | ||||
|           name: out-arm64-windows-2022 | ||||
|           name: out-windows-2022 | ||||
|           path: out | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
|         with: | ||||
|           name: out-x64-windows-2022 | ||||
|           name: out-macos-14 | ||||
|           path: out | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
|         with: | ||||
|           name: out-arm64-macos-14 | ||||
|           path: out | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
|         with: | ||||
|           name: out-x64-macos-14 | ||||
|           path: out | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
|         with: | ||||
|           name: out-arm64-ubuntu-22.04 | ||||
|           path: out | ||||
|  | ||||
|       - uses: actions/download-artifact@v3 | ||||
|         with: | ||||
|           name: out-x64-ubuntu-22.04 | ||||
|           name: out-ubuntu-22.04 | ||||
|           path: out | ||||
|  | ||||
|       - name: Generate the download static endpoint | ||||
|  | ||||
| @ -9,6 +9,7 @@ import { | ||||
|   executorInputPath, | ||||
| } from './test-utils' | ||||
| import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes' | ||||
| import { SETTINGS_FILE_NAME } from 'lib/constants' | ||||
| import { | ||||
|   TEST_SETTINGS_KEY, | ||||
|   TEST_SETTINGS_CORRUPTED, | ||||
| @ -343,7 +344,7 @@ test.describe('Testing settings', () => { | ||||
|  | ||||
|       // Selectors and constants | ||||
|       const errorHeading = page.getByRole('heading', { | ||||
|         name: 'An unextected error occurred', | ||||
|         name: 'An unexpected error occurred', | ||||
|       }) | ||||
|       const projectDirLink = page.getByText('Loaded from') | ||||
|  | ||||
| @ -372,7 +373,7 @@ test.describe('Testing settings', () => { | ||||
|  | ||||
|       // Selectors and constants | ||||
|       const errorHeading = page.getByRole('heading', { | ||||
|         name: 'An unextected error occurred', | ||||
|         name: 'An unexpected error occurred', | ||||
|       }) | ||||
|       const projectDirLink = page.getByText('Loaded from') | ||||
|  | ||||
| @ -384,6 +385,66 @@ test.describe('Testing settings', () => { | ||||
|     } | ||||
|   ) | ||||
|  | ||||
|   // It was much easier to test the logo color than the background stream color. | ||||
|   test( | ||||
|     'user settings reload on external change, on project and modeling view', | ||||
|     { tag: '@electron' }, | ||||
|     async ({ browserName }, testInfo) => { | ||||
|       const { | ||||
|         electronApp, | ||||
|         page, | ||||
|         dir: projectDirName, | ||||
|       } = await setupElectron({ | ||||
|         testInfo, | ||||
|         appSettings: { | ||||
|           app: { | ||||
|             // Doesn't matter what you set it to. It will | ||||
|             // default to 264.5 | ||||
|             themeColor: '0', | ||||
|           }, | ||||
|         }, | ||||
|       }) | ||||
|  | ||||
|       await page.setViewportSize({ width: 1200, height: 500 }) | ||||
|  | ||||
|       const logoLink = page.getByTestId('app-logo') | ||||
|       const projectDirLink = page.getByText('Loaded from') | ||||
|  | ||||
|       await test.step('Wait for project view', async () => { | ||||
|         await expect(projectDirLink).toBeVisible() | ||||
|         await expect(logoLink).toHaveCSS('--primary-hue', '264.5') | ||||
|       }) | ||||
|  | ||||
|       const changeColor = async (color: string) => { | ||||
|         const tempSettingsFilePath = join(projectDirName, SETTINGS_FILE_NAME) | ||||
|         let tomlStr = await fsp.readFile(tempSettingsFilePath, 'utf-8') | ||||
|         tomlStr = tomlStr.replace(/(themeColor = ")[0-9]+(")/, `$1${color}$2`) | ||||
|         await fsp.writeFile(tempSettingsFilePath, tomlStr) | ||||
|       } | ||||
|  | ||||
|       await test.step('Check color of logo changed', async () => { | ||||
|         await changeColor('99') | ||||
|         await expect(logoLink).toHaveCSS('--primary-hue', '99') | ||||
|       }) | ||||
|  | ||||
|       await test.step('Check color of logo changed when in modeling view', async () => { | ||||
|         await page.getByRole('button', { name: 'New project' }).click() | ||||
|         await page.getByTestId('project-link').first().click() | ||||
|         await page.getByRole('button', { name: 'Dismiss' }).click() | ||||
|         await changeColor('58') | ||||
|         await expect(logoLink).toHaveCSS('--primary-hue', '58') | ||||
|       }) | ||||
|  | ||||
|       await test.step('Check going back to projects view still changes the color', async () => { | ||||
|         await logoLink.click() | ||||
|         await expect(projectDirLink).toBeVisible() | ||||
|         await changeColor('21') | ||||
|         await expect(logoLink).toHaveCSS('--primary-hue', '21') | ||||
|       }) | ||||
|       await electronApp.close() | ||||
|     } | ||||
|   ) | ||||
|  | ||||
|   test( | ||||
|     `Closing settings modal should go back to the original file being viewed`, | ||||
|     { tag: '@electron' }, | ||||
|  | ||||
							
								
								
									
										1
									
								
								interface.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								interface.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -23,7 +23,6 @@ export interface IElectronAPI { | ||||
|     callback: (eventType: string, path: string) => void | ||||
|   ) => void | ||||
|   watchFileOff: (path: string) => void | ||||
|   watchFileObliterate: () => void | ||||
|   readFile: (path: string) => ReturnType<fs.readFile> | ||||
|   writeFile: ( | ||||
|     path: string, | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "zoo-modeling-app", | ||||
|   "version": "0.25.5", | ||||
|   "version": "0.25.6", | ||||
|   "private": true, | ||||
|   "productName": "Zoo Modeling App", | ||||
|   "author": { | ||||
| @ -36,6 +36,7 @@ | ||||
|     "@xstate/inspect": "^0.8.0", | ||||
|     "@xstate/react": "^4.1.1", | ||||
|     "bonjour-service": "^1.2.1", | ||||
|     "chokidar": "^4.0.1", | ||||
|     "codemirror": "^6.0.1", | ||||
|     "decamelize": "^6.0.0", | ||||
|     "electron-squirrel-startup": "^1.0.1", | ||||
|  | ||||
| @ -1,9 +1,10 @@ | ||||
| import { trap } from 'lib/trap' | ||||
| import { useMachine } from '@xstate/react' | ||||
| import { useNavigate, useRouteLoaderData, useLocation } from 'react-router-dom' | ||||
| import { PATHS } from 'lib/paths' | ||||
| import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine' | ||||
| import withBaseUrl from '../lib/withBaseURL' | ||||
| import React, { createContext, useEffect } from 'react' | ||||
| import React, { createContext, useEffect, useState } from 'react' | ||||
| import useStateMachineCommands from '../hooks/useStateMachineCommands' | ||||
| import { settingsMachine } from 'machines/settingsMachine' | ||||
| import { toast } from 'react-hot-toast' | ||||
| @ -15,7 +16,6 @@ import { | ||||
| } from 'lib/theme' | ||||
| import decamelize from 'decamelize' | ||||
| import { Actor, AnyStateMachine, ContextFrom, Prop, StateFrom } from 'xstate' | ||||
| import { isDesktop } from 'lib/isDesktop' | ||||
| import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig' | ||||
| import { | ||||
|   kclManager, | ||||
| @ -33,8 +33,14 @@ import { | ||||
| import { useCommandsContext } from 'hooks/useCommandsContext' | ||||
| import { Command } from 'lib/commandTypes' | ||||
| import { BaseUnit } from 'lib/settings/settingsTypes' | ||||
| import { saveSettings } from 'lib/settings/settingsUtils' | ||||
| import { | ||||
|   saveSettings, | ||||
|   loadAndValidateSettings, | ||||
| } from 'lib/settings/settingsUtils' | ||||
| import { reportRejection } from 'lib/trap' | ||||
| import { getAppSettingsFilePath } from 'lib/desktop' | ||||
| import { isDesktop } from 'lib/isDesktop' | ||||
| import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher' | ||||
|  | ||||
| type MachineContext<T extends AnyStateMachine> = { | ||||
|   state: StateFrom<T> | ||||
| @ -99,6 +105,9 @@ export const SettingsAuthProviderBase = ({ | ||||
|   const location = useLocation() | ||||
|   const navigate = useNavigate() | ||||
|   const { commandBarSend } = useCommandsContext() | ||||
|   const [settingsPath, setSettingsPath] = useState<string | undefined>( | ||||
|     undefined | ||||
|   ) | ||||
|  | ||||
|   const [settingsState, settingsSend, settingsActor] = useMachine( | ||||
|     settingsMachine.provide({ | ||||
| @ -191,7 +200,11 @@ export const SettingsAuthProviderBase = ({ | ||||
|             console.error('Error executing AST after settings change', e) | ||||
|           } | ||||
|         }, | ||||
|         persistSettings: ({ context }) => { | ||||
|         persistSettings: ({ context, event }) => { | ||||
|           // Without this, when a user changes the file, it'd | ||||
|           // create a detection loop with the file-system watcher. | ||||
|           if (event.doNotPersist) return | ||||
|  | ||||
|           // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||||
|           saveSettings(context, loadedProject?.project?.path) | ||||
|         }, | ||||
| @ -201,6 +214,23 @@ export const SettingsAuthProviderBase = ({ | ||||
|   ) | ||||
|   settingsStateRef = settingsState.context | ||||
|  | ||||
|   useEffect(() => { | ||||
|     if (!isDesktop()) return | ||||
|     getAppSettingsFilePath().then(setSettingsPath).catch(trap) | ||||
|   }, []) | ||||
|  | ||||
|   useFileSystemWatcher( | ||||
|     async () => { | ||||
|       const data = await loadAndValidateSettings(loadedProject?.project?.path) | ||||
|       settingsSend({ | ||||
|         type: 'Set all settings', | ||||
|         settings: data.settings, | ||||
|         doNotPersist: true, | ||||
|       }) | ||||
|     }, | ||||
|     settingsPath ? [settingsPath] : [] | ||||
|   ) | ||||
|  | ||||
|   // Add settings commands to the command bar | ||||
|   // They're treated slightly differently than other commands | ||||
|   // Because their state machine doesn't have a meaningful .nextEvents, | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import { isDesktop } from 'lib/isDesktop' | ||||
| import { reportRejection } from 'lib/trap' | ||||
| import { useEffect, useState, useRef } from 'react' | ||||
|  | ||||
| type Path = string | ||||
| @ -11,13 +12,13 @@ type Path = string | ||||
| // watcher.addListener(() => { ... }). | ||||
|  | ||||
| export const useFileSystemWatcher = ( | ||||
|   callback: (path: Path) => void, | ||||
|   callback: (path: Path) => Promise<void>, | ||||
|   dependencyArray: Path[] | ||||
| ): void => { | ||||
|   // Track a ref to the callback. This is how we get the callback updated | ||||
|   // across the NodeJS<->Browser boundary. | ||||
|   const callbackRef = useRef<{ fn: (path: Path) => void }>({ | ||||
|     fn: (_path) => {}, | ||||
|   const callbackRef = useRef<{ fn: (path: Path) => Promise<void> }>({ | ||||
|     fn: async (_path) => {}, | ||||
|   }) | ||||
|  | ||||
|   useEffect(() => { | ||||
| @ -35,7 +36,9 @@ export const useFileSystemWatcher = ( | ||||
|     if (!isDesktop()) return | ||||
|  | ||||
|     return () => { | ||||
|       window.electron.watchFileObliterate() | ||||
|       for (let path of dependencyArray) { | ||||
|         window.electron.watchFileOff(path) | ||||
|       } | ||||
|     } | ||||
|   }, []) | ||||
|  | ||||
| @ -46,6 +49,9 @@ export const useFileSystemWatcher = ( | ||||
|     ] | ||||
|   } | ||||
|  | ||||
|   const hasDiff = | ||||
|     difference(dependencyArray, dependencyArrayTracked)[0].length !== 0 | ||||
|  | ||||
|   // Removing 1 watcher at a time is only possible because in a filesystem, | ||||
|   // a path is unique (there can never be two paths with the same name). | ||||
|   // Otherwise we would have to obliterate() the whole list and reconstruct it. | ||||
| @ -53,6 +59,8 @@ export const useFileSystemWatcher = ( | ||||
|     // The hook is useless on web. | ||||
|     if (!isDesktop()) return | ||||
|  | ||||
|     if (!hasDiff) return | ||||
|  | ||||
|     const [pathsRemoved, pathsRemaining] = difference( | ||||
|       dependencyArrayTracked, | ||||
|       dependencyArray | ||||
| @ -62,10 +70,10 @@ export const useFileSystemWatcher = ( | ||||
|     } | ||||
|     const [pathsAdded] = difference(dependencyArray, dependencyArrayTracked) | ||||
|     for (let path of pathsAdded) { | ||||
|       window.electron.watchFileOn(path, (_eventType: string, path: Path) => | ||||
|         callbackRef.current.fn(path) | ||||
|       ) | ||||
|       window.electron.watchFileOn(path, (_eventType: string, path: Path) => { | ||||
|         callbackRef.current.fn(path).catch(reportRejection) | ||||
|       }) | ||||
|     } | ||||
|     setDependencyArrayTracked(pathsRemaining.concat(pathsAdded)) | ||||
|   }, [difference(dependencyArray, dependencyArrayTracked)[0].length !== 0]) | ||||
|   }, [hasDiff]) | ||||
| } | ||||
|  | ||||
| @ -379,7 +379,7 @@ const getAppFolderName = () => { | ||||
|   return window.electron.packageJson.name | ||||
| } | ||||
|  | ||||
| const getAppSettingsFilePath = async () => { | ||||
| export const getAppSettingsFilePath = async () => { | ||||
|   const isTestEnv = window.electron.process.env.IS_PLAYWRIGHT === 'true' | ||||
|   const testSettingsPath = window.electron.process.env.TEST_SETTINGS_FILE_KEY | ||||
|   const appConfig = await window.electron.getPath('appData') | ||||
|  | ||||
| @ -177,14 +177,14 @@ export async function loadAndValidateSettings( | ||||
|  | ||||
|   if (err(appSettingsPayload)) return Promise.reject(appSettingsPayload) | ||||
|  | ||||
|   const settings = createSettings() | ||||
|   let settingsNext = createSettings() | ||||
|   // Because getting the default directory is async, we need to set it after | ||||
|   if (onDesktop) { | ||||
|     settings.app.projectDirectory.default = await getInitialDefaultDir() | ||||
|   } | ||||
|  | ||||
|   setSettingsAtLevel( | ||||
|     settings, | ||||
|   settingsNext = setSettingsAtLevel( | ||||
|     settingsNext, | ||||
|     'user', | ||||
|     configurationToSettingsPayload(appSettingsPayload) | ||||
|   ) | ||||
| @ -199,8 +199,8 @@ export async function loadAndValidateSettings( | ||||
|       return Promise.reject(new Error('Invalid project settings')) | ||||
|  | ||||
|     const projectSettingsPayload = projectSettings | ||||
|     setSettingsAtLevel( | ||||
|       settings, | ||||
|     settingsNext = setSettingsAtLevel( | ||||
|       settingsNext, | ||||
|       'project', | ||||
|       projectConfigurationToSettingsPayload(projectSettingsPayload) | ||||
|     ) | ||||
| @ -208,7 +208,7 @@ export async function loadAndValidateSettings( | ||||
|  | ||||
|   // Return the settings object | ||||
|   return { | ||||
|     settings, | ||||
|     settings: settingsNext, | ||||
|     configuration: appSettingsPayload, | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,7 @@ export const settingsMachine = setup({ | ||||
|   types: { | ||||
|     context: {} as ReturnType<typeof createSettings>, | ||||
|     input: {} as ReturnType<typeof createSettings>, | ||||
|     events: {} as | ||||
|     events: {} as ( | ||||
|       | WildcardSetEvent<SettingsPaths> | ||||
|       | SetEventTypes | ||||
|       | { | ||||
| @ -34,7 +34,8 @@ export const settingsMachine = setup({ | ||||
|           type: 'Reset settings' | ||||
|           level: SettingsLevel | ||||
|         } | ||||
|       | { type: 'Set all settings'; settings: typeof settings }, | ||||
|       | { type: 'Set all settings'; settings: typeof settings } | ||||
|     ) & { doNotPersist?: boolean }, | ||||
|   }, | ||||
|   actions: { | ||||
|     setEngineTheme: () => {}, | ||||
|  | ||||
| @ -5,6 +5,7 @@ import os from 'node:os' | ||||
| import fsSync from 'node:fs' | ||||
| import packageJson from '../package.json' | ||||
| import { MachinesListing } from 'lib/machineManager' | ||||
| import chokidar from 'chokidar' | ||||
|  | ||||
| const open = (args: any) => ipcRenderer.invoke('dialog.showOpenDialog', args) | ||||
| const save = (args: any) => ipcRenderer.invoke('dialog.showSaveDialog', args) | ||||
| @ -23,36 +24,21 @@ const isMac = os.platform() === 'darwin' | ||||
| const isWindows = os.platform() === 'win32' | ||||
| const isLinux = os.platform() === 'linux' | ||||
|  | ||||
| let fsWatchListeners = new Map< | ||||
|   string, | ||||
|   { | ||||
|     watcher: fsSync.FSWatcher | ||||
|     callback: (eventType: string, path: string) => void | ||||
|   } | ||||
| >() | ||||
| let fsWatchListeners = new Map<string, ReturnType<typeof chokidar.watch>>() | ||||
|  | ||||
| const watchFileOn = ( | ||||
|   path: string, | ||||
|   callback: (eventType: string, path: string) => void | ||||
| ) => { | ||||
|   const watcher = fsSync.watch(path) | ||||
|   watcher.on('change', callback) | ||||
|   fsWatchListeners.set(path, { watcher, callback }) | ||||
| const watchFileOn = (path: string, callback: (path: string) => void) => { | ||||
|   const watcherMaybe = fsWatchListeners.get(path) | ||||
|   if (watcherMaybe) return | ||||
|   const watcher = chokidar.watch(path) | ||||
|   watcher.on('all', callback) | ||||
|   fsWatchListeners.set(path, watcher) | ||||
| } | ||||
| const watchFileOff = (path: string) => { | ||||
|   const entry = fsWatchListeners.get(path) | ||||
|   if (!entry) return | ||||
|   const { watcher, callback } = entry | ||||
|   watcher.off('change', callback) | ||||
|   watcher.close() | ||||
|   const watcher = fsWatchListeners.get(path) | ||||
|   if (!watcher) return | ||||
|   watcher.unwatch(path) | ||||
|   fsWatchListeners.delete(path) | ||||
| } | ||||
| const watchFileObliterate = () => { | ||||
|   for (let [pathAsKey] of fsWatchListeners) { | ||||
|     watchFileOff(pathAsKey) | ||||
|   } | ||||
|   fsWatchListeners = new Map() | ||||
| } | ||||
| const readFile = (path: string) => fs.readFile(path, 'utf-8') | ||||
| // It seems like from the node source code this does not actually block but also | ||||
| // don't trust me on that (jess). | ||||
| @ -103,7 +89,6 @@ contextBridge.exposeInMainWorld('electron', { | ||||
|   // exported. | ||||
|   watchFileOn, | ||||
|   watchFileOff, | ||||
|   watchFileObliterate, | ||||
|   readFile, | ||||
|   writeFile, | ||||
|   exists, | ||||
|  | ||||
| @ -57,7 +57,7 @@ const Home = () => { | ||||
|  | ||||
|   // Re-read projects listing if the projectDir has any updates. | ||||
|   useFileSystemWatcher( | ||||
|     () => { | ||||
|     async () => { | ||||
|       setProjectsLoaderTrigger(projectsLoaderTrigger + 1) | ||||
|     }, | ||||
|     projectsDir ? [projectsDir] : [] | ||||
|  | ||||
							
								
								
									
										43
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										43
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -934,9 +934,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" | ||||
|  | ||||
| [[package]] | ||||
| name = "futures" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" | ||||
| checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" | ||||
| dependencies = [ | ||||
|  "futures-channel", | ||||
|  "futures-core", | ||||
| @ -949,9 +949,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-channel" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" | ||||
| checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "futures-sink", | ||||
| @ -959,15 +959,15 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-core" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" | ||||
| checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-executor" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" | ||||
| checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "futures-task", | ||||
| @ -976,15 +976,15 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-io" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" | ||||
| checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-macro" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" | ||||
| checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
| @ -993,21 +993,21 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-sink" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" | ||||
| checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-task" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" | ||||
| checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" | ||||
|  | ||||
| [[package]] | ||||
| name = "futures-util" | ||||
| version = "0.3.30" | ||||
| version = "0.3.31" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" | ||||
| checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" | ||||
| dependencies = [ | ||||
|  "futures-channel", | ||||
|  "futures-core", | ||||
| @ -1966,12 +1966,9 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "once_cell" | ||||
| version = "1.20.1" | ||||
| version = "1.20.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" | ||||
| dependencies = [ | ||||
|  "portable-atomic", | ||||
| ] | ||||
| checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" | ||||
|  | ||||
| [[package]] | ||||
| name = "oncemutex" | ||||
|  | ||||
| @ -35,7 +35,7 @@ uuid = { version = "1.10.0", features = ["v4", "js", "serde"] } | ||||
|  | ||||
| [target.'cfg(target_arch = "wasm32")'.dependencies] | ||||
| console_error_panic_hook = "0.1.7" | ||||
| futures = "0.3.30" | ||||
| futures = "0.3.31" | ||||
| js-sys = "0.3.69" | ||||
| tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] } | ||||
| wasm-bindgen-futures = { version = "0.4.41", features = ["futures-core-03-stream"] } | ||||
|  | ||||
| @ -14,7 +14,7 @@ proc-macro = true | ||||
| [dependencies] | ||||
| Inflector = "0.11.4" | ||||
| convert_case = "0.6.0" | ||||
| once_cell = "1.19.0" | ||||
| once_cell = "1.20.2" | ||||
| proc-macro2 = "1" | ||||
| quote = "1" | ||||
| regex = "1.10" | ||||
|  | ||||
| @ -22,7 +22,7 @@ dashmap = "6.1.0" | ||||
| databake = { version = "0.1.8", features = ["derive"] } | ||||
| derive-docs = { version = "0.1.29", path = "../derive-docs" } | ||||
| form_urlencoded = "1.2.1" | ||||
| futures = { version = "0.3.30" } | ||||
| futures = { version = "0.3.31" } | ||||
| git_rev = "0.1.0" | ||||
| gltf-json = "1.4.1" | ||||
| http = { workspace = true } | ||||
|  | ||||
							
								
								
									
										43
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								yarn.lock
									
									
									
									
									
								
							| @ -3780,6 +3780,13 @@ chokidar@^3.5.3: | ||||
|   optionalDependencies: | ||||
|     fsevents "~2.3.2" | ||||
|  | ||||
| chokidar@^4.0.1: | ||||
|   version "4.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" | ||||
|   integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA== | ||||
|   dependencies: | ||||
|     readdirp "^4.0.1" | ||||
|  | ||||
| chownr@^2.0.0: | ||||
|   version "2.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" | ||||
| @ -8155,6 +8162,11 @@ readable-stream@~2.3.6: | ||||
|     string_decoder "~1.1.1" | ||||
|     util-deprecate "~1.0.1" | ||||
|  | ||||
| readdirp@^4.0.1: | ||||
|   version "4.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" | ||||
|   integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== | ||||
|  | ||||
| readdirp@~3.6.0: | ||||
|   version "3.6.0" | ||||
|   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" | ||||
| @ -8773,16 +8785,7 @@ string-natural-compare@^3.0.1: | ||||
|   resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" | ||||
|   integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== | ||||
|  | ||||
| "string-width-cjs@npm:string-width@^4.2.0": | ||||
|   version "4.2.3" | ||||
|   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" | ||||
|   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== | ||||
|   dependencies: | ||||
|     emoji-regex "^8.0.0" | ||||
|     is-fullwidth-code-point "^3.0.0" | ||||
|     strip-ansi "^6.0.1" | ||||
|  | ||||
| "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: | ||||
| "string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: | ||||
|   version "4.2.3" | ||||
|   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" | ||||
|   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== | ||||
| @ -8876,14 +8879,7 @@ string_decoder@~1.1.1: | ||||
|   dependencies: | ||||
|     safe-buffer "~5.1.0" | ||||
|  | ||||
| "strip-ansi-cjs@npm:strip-ansi@^6.0.1": | ||||
|   version "6.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" | ||||
|   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== | ||||
|   dependencies: | ||||
|     ansi-regex "^5.0.1" | ||||
|  | ||||
| strip-ansi@^6.0.0, strip-ansi@^6.0.1: | ||||
| "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: | ||||
|   version "6.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" | ||||
|   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== | ||||
| @ -9757,16 +9753,7 @@ word-wrap@^1.2.3, word-wrap@^1.2.5: | ||||
|   resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" | ||||
|   integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== | ||||
|  | ||||
| "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": | ||||
|   version "7.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" | ||||
|   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== | ||||
|   dependencies: | ||||
|     ansi-styles "^4.0.0" | ||||
|     string-width "^4.1.0" | ||||
|     strip-ansi "^6.0.0" | ||||
|  | ||||
| wrap-ansi@^7.0.0: | ||||
| "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: | ||||
|   version "7.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" | ||||
|   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== | ||||
|  | ||||
		Reference in New Issue
	
	Block a user