@caperjs/plugin-tauri 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caper-plugin-tauri.mjs","names":[],"sources":["../src/version.ts","../src/TauriPlugin.ts","../src/index.ts"],"sourcesContent":["export const version = '0.7.2';\n","import {\n type FullscreenDriver,\n type IApplication,\n type IPlugin,\n isAndroid,\n isDev,\n isMobile,\n isTauri,\n Logger,\n Plugin,\n} from '@caperjs/core';\nimport type { UnlistenFn } from '@tauri-apps/api/event';\nimport type { Window as TauriWindow } from '@tauri-apps/api/window';\nimport type { Store as TauriStore } from '@tauri-apps/plugin-store';\nimport { version } from './version';\n\n/** Options for {@link TauriPlugin}, all optional. */\nexport interface TauriPluginOptions {\n /** Pause the app while the webview is hidden/occluded. Default `true`. */\n pauseOnHide?: boolean;\n /** Pause the app while the native window is unfocused. Default `false`. */\n pauseOnBlur?: boolean;\n /** Drive `app.fullScreen` through the native window. Default `true`. */\n nativeFullscreen?: boolean;\n /** Swallow the webview's right-click menu. Default: `true` in production, `false` in dev. */\n disableContextMenu?: boolean;\n /** File the disk-backed key/value store lives in, under the app data dir. Default `'caper-save.json'`. */\n storeFile?: string;\n /**\n * Android only: the keyboard key a back-button press (or back gesture) is delivered\n * as. Bind it in your controls config like any key, e.g.\n * `close: ['Escape', 'GoBack']`, `toggle_pause: ['P', 'GoBack']`; action contexts\n * decide which one fires. While this is on, back never closes the app by itself.\n * `false` leaves Android's default behavior alone. Default `'GoBack'` (the DOM\n * standard name for this key).\n */\n backButtonKey?: string | false;\n}\n\n/**\n * Public contract for the Tauri plugin. `save`/`load` make it storage-capable, so\n * it can be named as a `app.store` adapter — on native *and* on the web.\n */\nexport interface ITauriPlugin extends IPlugin<TauriPluginOptions> {\n /** Whether the app is actually running inside a Tauri webview. */\n readonly isNative: boolean;\n save(key: string, data: any): Promise<void>;\n load<T = any>(key: string): Promise<T | undefined>;\n /** Close the native window. A logged no-op on the web. */\n quit(): Promise<void>;\n}\n\n/** Prefix for the web (`localStorage`) fallback keys. */\nconst WEB_KEY_PREFIX = 'caper:';\n\n/**\n * Read at `initialize` time rather than module-eval time so `isDev` reflects the\n * build the plugin actually runs in.\n */\nfunction defaultOptions(): Required<TauriPluginOptions> {\n return {\n pauseOnHide: true,\n pauseOnBlur: false,\n nativeFullscreen: true,\n disableContextMenu: !isDev,\n storeFile: 'caper-save.json',\n backButtonKey: 'GoBack',\n };\n}\n\n/**\n * Native-shell integration for apps packaged with Tauri v2.\n *\n * Everything native is reached through `await import(...)` behind an `isTauri`\n * guard, so the same `plugins: [...]` list loads unchanged on the plain web —\n * where the plugin is inert apart from `save`/`load`, which fall back to\n * `localStorage`.\n *\n * What it does on native:\n * - pauses the app when the webview is hidden (a hidden Tauri window freezes\n * `requestAnimationFrame` and timers outright), and optionally on blur;\n * - installs a `FullscreenDriver` on `app.fullScreen`, because Tauri's webview\n * has no HTML Fullscreen API at all;\n * - backs `save`/`load` with `@tauri-apps/plugin-store`, a real file on disk,\n * rather than `localStorage` (which WebKit may evict);\n * - suppresses the webview's own right-click menu.\n *\n * @example\n * ```ts\n * // caper.config.ts\n * plugins: [['tauri', { options: { pauseOnBlur: true } }]],\n *\n * // anywhere\n * await app.store.save('tauri', 'progress', { level: 3 });\n * ```\n */\nexport class TauriPlugin extends Plugin<TauriPluginOptions> implements ITauriPlugin {\n public readonly id = 'tauri';\n /** `app.fullScreen` must exist before `postInitialize` installs the driver. */\n public readonly requires = ['fullscreen'];\n\n protected _options: Required<TauriPluginOptions>;\n\n /** The native window handle, resolved once on native. */\n private _window: TauriWindow | null = null;\n /** In-flight/settled store open, so the file is opened exactly once. */\n private _storePromise: Promise<TauriStore> | null = null;\n /** Whether *this* plugin is the one that paused the app. */\n private _didPause = false;\n /** The `notify` callback handed over by the fullscreen driver's `subscribe`. */\n private _notifyFullscreen: ((isFullscreen: boolean) => void) | null = null;\n /** So the off-native `quit()` warning is logged once, not once per call. */\n private _warnedQuit = false;\n\n /** Whether the app is actually running inside a Tauri webview. */\n public get isNative(): boolean {\n return isTauri;\n }\n\n public async initialize(options: Partial<TauriPluginOptions>, _app: IApplication): Promise<void> {\n this._options = { ...defaultOptions(), ...options };\n\n if (!isTauri) {\n return;\n }\n\n if (this._options.disableContextMenu) {\n this.listen(document, 'contextmenu', (event) => event.preventDefault());\n }\n\n if (this._options.pauseOnHide) {\n this.listen(document, 'visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this._pause();\n } else {\n this._resume();\n }\n });\n }\n\n const win = await this._nativeWindow();\n\n if (this._options.pauseOnBlur) {\n this.addDisposer(\n await win.onFocusChanged(({ payload: focused }) => {\n if (focused) {\n this._resume();\n } else {\n this._pause();\n }\n }),\n );\n }\n\n await this._listenLifecycle();\n await this._listenBackButton();\n }\n\n public async postInitialize(_app: IApplication): Promise<void> {\n // Desktop only. Tauri's window.setFullscreen() rejects on Android/iOS, where the\n // webview's own HTML Fullscreen API works (and the Android shell already hides the\n // system bars), so on mobile core's DOM path is the one to keep.\n if (!isTauri || isMobile || !this._options.nativeFullscreen) {\n return;\n }\n\n const fullScreen = this.app.fullScreen;\n const win = await this._nativeWindow();\n\n // Tauri has no fullscreen-change event; a resize always accompanies the\n // transition, so re-query on resize and report only what changed.\n this.addDisposer(\n await win.onResized(async () => {\n if (!this._notifyFullscreen) {\n return;\n }\n try {\n this._notifyFullscreen(await win.isFullscreen());\n } catch (error) {\n Logger.error(`[${this.id}] could not read the native fullscreen state:`, error);\n }\n }),\n );\n\n const driver: FullscreenDriver = {\n supported: true,\n request: () => win.setFullscreen(true),\n exit: () => win.setFullscreen(false),\n subscribe: (notify) => {\n this._notifyFullscreen = notify;\n return () => {\n this._notifyFullscreen = null;\n };\n },\n };\n fullScreen.setFullscreenDriver(driver);\n this.addDisposer(() => fullScreen.setFullscreenDriver(null));\n }\n\n /**\n * Persist `data` under `key` — to the disk-backed store on native, to\n * `localStorage` on the web. Rejects (never swallows) on failure.\n */\n public async save(key: string, data: any): Promise<void> {\n if (!isTauri) {\n try {\n localStorage.setItem(`${WEB_KEY_PREFIX}${key}`, JSON.stringify(data));\n } catch (error) {\n throw new Error(`[${this.id}] failed to save \"${key}\" to localStorage: ${describe(error)}`);\n }\n return;\n }\n\n try {\n const store = await this._store();\n await store.set(key, data);\n await store.save();\n } catch (error) {\n throw new Error(`[${this.id}] failed to save \"${key}\" to ${this._options.storeFile}: ${describe(error)}`);\n }\n }\n\n /**\n * Read back what `save` wrote. A missing key resolves `undefined`; so does a\n * corrupt `localStorage` entry on the web.\n */\n public async load<T = any>(key: string): Promise<T | undefined> {\n if (!isTauri) {\n const raw = localStorage.getItem(`${WEB_KEY_PREFIX}${key}`);\n if (raw === null) {\n return undefined;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n\n try {\n const store = await this._store();\n return await store.get<T>(key);\n } catch (error) {\n throw new Error(`[${this.id}] failed to load \"${key}\" from ${this._options.storeFile}: ${describe(error)}`);\n }\n }\n\n /** Close the native window. A logged no-op on the web. */\n public async quit(): Promise<void> {\n if (!isTauri) {\n if (!this._warnedQuit) {\n this._warnedQuit = true;\n Logger.warn(`[${this.id}] quit() does nothing outside a Tauri window`);\n }\n return;\n }\n const win = await this._nativeWindow();\n await win.close();\n }\n\n /** Resolve (once) the native window handle. Only ever called when `isTauri`. */\n private async _nativeWindow(): Promise<TauriWindow> {\n if (!this._window) {\n const { getCurrentWindow } = await import('@tauri-apps/api/window');\n this._window = getCurrentWindow();\n }\n return this._window;\n }\n\n /** Open (once) the disk-backed store. Only ever called when `isTauri`. */\n private _store(): Promise<TauriStore> {\n if (!this._storePromise) {\n this._storePromise = (async () => {\n const { load } = await import('@tauri-apps/plugin-store');\n return load(this._options.storeFile, { autoSave: true });\n })();\n // a failed open must not be cached, or every later call sees the same error\n this._storePromise.catch(() => {\n this._storePromise = null;\n });\n }\n return this._storePromise;\n }\n\n /**\n * Mobile webviews are suspended rather than hidden, so `visibilitychange` is\n * not enough on iOS/Android. These events exist from `@tauri-apps/api` v2.\n */\n /**\n * Android's back button, delivered as an ordinary key press. Caper's keyboard\n * controls read keys off the document, so the game's controls config and action\n * contexts decide what back does; nothing is hard-coded here.\n */\n private async _listenBackButton(): Promise<void> {\n const key = this._options.backButtonKey;\n if (!isAndroid || !key) {\n return;\n }\n try {\n const { onBackButtonPress } = await import('@tauri-apps/api/app');\n const listener = await onBackButtonPress(() => {\n document.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }));\n document.dispatchEvent(new KeyboardEvent('keyup', { key, bubbles: true }));\n });\n this.addDisposer(() => void listener.unregister());\n } catch (error) {\n Logger.error(`[${this.id}] could not listen for the Android back button:`, error);\n }\n }\n\n private async _listenLifecycle(): Promise<void> {\n const { listen, TauriEvent } = await import('@tauri-apps/api/event');\n const suspended = (TauriEvent as Record<string, string>).WINDOW_SUSPENDED;\n const resumed = (TauriEvent as Record<string, string>).WINDOW_RESUMED;\n if (!suspended || !resumed) {\n return;\n }\n const unlisten: UnlistenFn[] = await Promise.all([\n listen(suspended, () => this._pause()),\n listen(resumed, () => this._resume()),\n ]);\n this.addDisposer(...unlisten);\n }\n\n /** Pause the app, but only if nothing else already did — and remember that we did. */\n private _pause(): void {\n if (this.app.paused) {\n return;\n }\n this.app.pause();\n this._didPause = true;\n }\n\n /** Resume the app, but only if this plugin is the one that paused it. */\n private _resume(): void {\n if (!this._didPause) {\n return;\n }\n this._didPause = false;\n this.app.resume();\n }\n}\n\n/** Message text from whatever a rejected Tauri call threw. */\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport { version };\nexport default TauriPlugin;\n","import { TauriPlugin } from './TauriPlugin';\n\nexport * from './TauriPlugin';\n\nexport default TauriPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"caper-plugin-tauri.mjs","names":[],"sources":["../src/version.ts","../src/TauriPlugin.ts","../src/index.ts"],"sourcesContent":["export const version = '0.7.4';\n","import {\n type FullscreenDriver,\n type IApplication,\n type IPlugin,\n isAndroid,\n isDev,\n isMobile,\n isTauri,\n Logger,\n Plugin,\n} from '@caperjs/core';\nimport type { UnlistenFn } from '@tauri-apps/api/event';\nimport type { Window as TauriWindow } from '@tauri-apps/api/window';\nimport type { Store as TauriStore } from '@tauri-apps/plugin-store';\nimport { version } from './version';\n\n/** Options for {@link TauriPlugin}, all optional. */\nexport interface TauriPluginOptions {\n /** Pause the app while the webview is hidden/occluded. Default `true`. */\n pauseOnHide?: boolean;\n /** Pause the app while the native window is unfocused. Default `false`. */\n pauseOnBlur?: boolean;\n /** Drive `app.fullScreen` through the native window. Default `true`. */\n nativeFullscreen?: boolean;\n /** Swallow the webview's right-click menu. Default: `true` in production, `false` in dev. */\n disableContextMenu?: boolean;\n /** File the disk-backed key/value store lives in, under the app data dir. Default `'caper-save.json'`. */\n storeFile?: string;\n /**\n * Android only: the keyboard key a back-button press (or back gesture) is delivered\n * as. Bind it in your controls config like any key, e.g.\n * `close: ['Escape', 'GoBack']`, `toggle_pause: ['P', 'GoBack']`; action contexts\n * decide which one fires. While this is on, back never closes the app by itself.\n * `false` leaves Android's default behavior alone. Default `'GoBack'` (the DOM\n * standard name for this key).\n */\n backButtonKey?: string | false;\n}\n\n/**\n * Public contract for the Tauri plugin. `save`/`load` make it storage-capable, so\n * it can be named as a `app.store` adapter — on native *and* on the web.\n */\nexport interface ITauriPlugin extends IPlugin<TauriPluginOptions> {\n /** Whether the app is actually running inside a Tauri webview. */\n readonly isNative: boolean;\n save(key: string, data: any): Promise<void>;\n load<T = any>(key: string): Promise<T | undefined>;\n /** Close the native window. A logged no-op on the web. */\n quit(): Promise<void>;\n}\n\n/** Prefix for the web (`localStorage`) fallback keys. */\nconst WEB_KEY_PREFIX = 'caper:';\n\n/**\n * Read at `initialize` time rather than module-eval time so `isDev` reflects the\n * build the plugin actually runs in.\n */\nfunction defaultOptions(): Required<TauriPluginOptions> {\n return {\n pauseOnHide: true,\n pauseOnBlur: false,\n nativeFullscreen: true,\n disableContextMenu: !isDev,\n storeFile: 'caper-save.json',\n backButtonKey: 'GoBack',\n };\n}\n\n/**\n * Native-shell integration for apps packaged with Tauri v2.\n *\n * Everything native is reached through `await import(...)` behind an `isTauri`\n * guard, so the same `plugins: [...]` list loads unchanged on the plain web —\n * where the plugin is inert apart from `save`/`load`, which fall back to\n * `localStorage`.\n *\n * What it does on native:\n * - pauses the app when the webview is hidden (a hidden Tauri window freezes\n * `requestAnimationFrame` and timers outright), and optionally on blur;\n * - installs a `FullscreenDriver` on `app.fullScreen`, because Tauri's webview\n * has no HTML Fullscreen API at all;\n * - backs `save`/`load` with `@tauri-apps/plugin-store`, a real file on disk,\n * rather than `localStorage` (which WebKit may evict);\n * - suppresses the webview's own right-click menu.\n *\n * @example\n * ```ts\n * // caper.config.ts\n * plugins: [['tauri', { options: { pauseOnBlur: true } }]],\n *\n * // anywhere\n * await app.store.save('tauri', 'progress', { level: 3 });\n * ```\n */\nexport class TauriPlugin extends Plugin<TauriPluginOptions> implements ITauriPlugin {\n public readonly id = 'tauri';\n /** `app.fullScreen` must exist before `postInitialize` installs the driver. */\n public readonly requires = ['fullscreen'];\n\n protected _options: Required<TauriPluginOptions>;\n\n /** The native window handle, resolved once on native. */\n private _window: TauriWindow | null = null;\n /** In-flight/settled store open, so the file is opened exactly once. */\n private _storePromise: Promise<TauriStore> | null = null;\n /** Whether *this* plugin is the one that paused the app. */\n private _didPause = false;\n /** The `notify` callback handed over by the fullscreen driver's `subscribe`. */\n private _notifyFullscreen: ((isFullscreen: boolean) => void) | null = null;\n /** So the off-native `quit()` warning is logged once, not once per call. */\n private _warnedQuit = false;\n\n /** Whether the app is actually running inside a Tauri webview. */\n public get isNative(): boolean {\n return isTauri;\n }\n\n public async initialize(options: Partial<TauriPluginOptions>, _app: IApplication): Promise<void> {\n this._options = { ...defaultOptions(), ...options };\n\n if (!isTauri) {\n return;\n }\n\n if (this._options.disableContextMenu) {\n this.listen(document, 'contextmenu', (event) => event.preventDefault());\n }\n\n if (this._options.pauseOnHide) {\n this.listen(document, 'visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this._pause();\n } else {\n this._resume();\n }\n });\n }\n\n const win = await this._nativeWindow();\n\n if (this._options.pauseOnBlur) {\n this.addDisposer(\n await win.onFocusChanged(({ payload: focused }) => {\n if (focused) {\n this._resume();\n } else {\n this._pause();\n }\n }),\n );\n }\n\n await this._listenLifecycle();\n await this._listenBackButton();\n }\n\n public async postInitialize(_app: IApplication): Promise<void> {\n // Desktop only. Tauri's window.setFullscreen() rejects on Android/iOS, where the\n // webview's own HTML Fullscreen API works (and the Android shell already hides the\n // system bars), so on mobile core's DOM path is the one to keep.\n if (!isTauri || isMobile || !this._options.nativeFullscreen) {\n return;\n }\n\n const fullScreen = this.app.fullScreen;\n const win = await this._nativeWindow();\n\n // Tauri has no fullscreen-change event; a resize always accompanies the\n // transition, so re-query on resize and report only what changed.\n this.addDisposer(\n await win.onResized(async () => {\n if (!this._notifyFullscreen) {\n return;\n }\n try {\n this._notifyFullscreen(await win.isFullscreen());\n } catch (error) {\n Logger.error(`[${this.id}] could not read the native fullscreen state:`, error);\n }\n }),\n );\n\n const driver: FullscreenDriver = {\n supported: true,\n request: () => win.setFullscreen(true),\n exit: () => win.setFullscreen(false),\n subscribe: (notify) => {\n this._notifyFullscreen = notify;\n return () => {\n this._notifyFullscreen = null;\n };\n },\n };\n fullScreen.setFullscreenDriver(driver);\n this.addDisposer(() => fullScreen.setFullscreenDriver(null));\n }\n\n /**\n * Persist `data` under `key` — to the disk-backed store on native, to\n * `localStorage` on the web. Rejects (never swallows) on failure.\n */\n public async save(key: string, data: any): Promise<void> {\n if (!isTauri) {\n try {\n localStorage.setItem(`${WEB_KEY_PREFIX}${key}`, JSON.stringify(data));\n } catch (error) {\n throw new Error(`[${this.id}] failed to save \"${key}\" to localStorage: ${describe(error)}`);\n }\n return;\n }\n\n try {\n const store = await this._store();\n await store.set(key, data);\n await store.save();\n } catch (error) {\n throw new Error(`[${this.id}] failed to save \"${key}\" to ${this._options.storeFile}: ${describe(error)}`);\n }\n }\n\n /**\n * Read back what `save` wrote. A missing key resolves `undefined`; so does a\n * corrupt `localStorage` entry on the web.\n */\n public async load<T = any>(key: string): Promise<T | undefined> {\n if (!isTauri) {\n const raw = localStorage.getItem(`${WEB_KEY_PREFIX}${key}`);\n if (raw === null) {\n return undefined;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n return undefined;\n }\n }\n\n try {\n const store = await this._store();\n return await store.get<T>(key);\n } catch (error) {\n throw new Error(`[${this.id}] failed to load \"${key}\" from ${this._options.storeFile}: ${describe(error)}`);\n }\n }\n\n /** Close the native window. A logged no-op on the web. */\n public async quit(): Promise<void> {\n if (!isTauri) {\n if (!this._warnedQuit) {\n this._warnedQuit = true;\n Logger.warn(`[${this.id}] quit() does nothing outside a Tauri window`);\n }\n return;\n }\n const win = await this._nativeWindow();\n await win.close();\n }\n\n /** Resolve (once) the native window handle. Only ever called when `isTauri`. */\n private async _nativeWindow(): Promise<TauriWindow> {\n if (!this._window) {\n const { getCurrentWindow } = await import('@tauri-apps/api/window');\n this._window = getCurrentWindow();\n }\n return this._window;\n }\n\n /** Open (once) the disk-backed store. Only ever called when `isTauri`. */\n private _store(): Promise<TauriStore> {\n if (!this._storePromise) {\n this._storePromise = (async () => {\n const { load } = await import('@tauri-apps/plugin-store');\n return load(this._options.storeFile, { autoSave: true });\n })();\n // a failed open must not be cached, or every later call sees the same error\n this._storePromise.catch(() => {\n this._storePromise = null;\n });\n }\n return this._storePromise;\n }\n\n /**\n * Mobile webviews are suspended rather than hidden, so `visibilitychange` is\n * not enough on iOS/Android. These events exist from `@tauri-apps/api` v2.\n */\n /**\n * Android's back button, delivered as an ordinary key press. Caper's keyboard\n * controls read keys off the document, so the game's controls config and action\n * contexts decide what back does; nothing is hard-coded here.\n */\n private async _listenBackButton(): Promise<void> {\n const key = this._options.backButtonKey;\n if (!isAndroid || !key) {\n return;\n }\n try {\n const { onBackButtonPress } = await import('@tauri-apps/api/app');\n const listener = await onBackButtonPress(() => {\n document.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }));\n document.dispatchEvent(new KeyboardEvent('keyup', { key, bubbles: true }));\n });\n this.addDisposer(() => void listener.unregister());\n } catch (error) {\n Logger.error(`[${this.id}] could not listen for the Android back button:`, error);\n }\n }\n\n private async _listenLifecycle(): Promise<void> {\n const { listen, TauriEvent } = await import('@tauri-apps/api/event');\n const suspended = (TauriEvent as Record<string, string>).WINDOW_SUSPENDED;\n const resumed = (TauriEvent as Record<string, string>).WINDOW_RESUMED;\n if (!suspended || !resumed) {\n return;\n }\n const unlisten: UnlistenFn[] = await Promise.all([\n listen(suspended, () => this._pause()),\n listen(resumed, () => this._resume()),\n ]);\n this.addDisposer(...unlisten);\n }\n\n /** Pause the app, but only if nothing else already did — and remember that we did. */\n private _pause(): void {\n if (this.app.paused) {\n return;\n }\n this.app.pause();\n this._didPause = true;\n }\n\n /** Resume the app, but only if this plugin is the one that paused it. */\n private _resume(): void {\n if (!this._didPause) {\n return;\n }\n this._didPause = false;\n this.app.resume();\n }\n}\n\n/** Message text from whatever a rejected Tauri call threw. */\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport { version };\nexport default TauriPlugin;\n","import { TauriPlugin } from './TauriPlugin';\n\nexport * from './TauriPlugin';\n\nexport default TauriPlugin;\n"],"mappings":"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"}
|
package/lib/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "0.7.
|
|
1
|
+
export declare const version = "0.7.4";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"@caperjs/core",
|
|
9
9
|
"caper"
|
|
10
10
|
],
|
|
11
|
-
"version": "0.7.
|
|
11
|
+
"version": "0.7.4",
|
|
12
12
|
"files": [
|
|
13
13
|
"lib",
|
|
14
14
|
"src",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"author": "",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@caperjs/core": "^0.7.
|
|
34
|
+
"@caperjs/core": "^0.7.4",
|
|
35
35
|
"@tauri-apps/api": "^2",
|
|
36
36
|
"@tauri-apps/plugin-store": "^2",
|
|
37
37
|
"happy-dom": "^15",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"vitest": "^3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@caperjs/core": "^0.7.
|
|
43
|
+
"@caperjs/core": "^0.7.4",
|
|
44
44
|
"@tauri-apps/api": "^2",
|
|
45
45
|
"@tauri-apps/plugin-store": "^2"
|
|
46
46
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.7.
|
|
1
|
+
export const version = '0.7.4';
|