From d2df1872059678bf6ccc2a4db38a14f066c76d0f Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Tue, 2 Sep 2025 15:00:21 +0530 Subject: [PATCH 1/7] fix(api): resolve TypeScript errors in webviewWindow.ts Fix interface conflicts between WebviewOptions and WindowOptions by properly handling type intersections and adding missing properties to internal types. --- packages/api/src/webviewWindow.ts | 139 +++++++----------------------- 1 file changed, 30 insertions(+), 109 deletions(-) diff --git a/packages/api/src/webviewWindow.ts b/packages/api/src/webviewWindow.ts index f73d5cab58a8..546e9b74a9f4 100644 --- a/packages/api/src/webviewWindow.ts +++ b/packages/api/src/webviewWindow.ts @@ -15,6 +15,13 @@ import type { EventName, EventCallback, UnlistenFn } from './event' import { invoke } from './core' import type { Color, DragDropEvent } from './webview' +// Internal-only type to avoid @ts-expect-error everywhere +interface InternalWebviewOptions + extends Omit { + skip?: boolean + parent?: Window | WebviewWindow | string +} + /** * Get an instance of `Webview` for the current webview window. * @@ -22,8 +29,7 @@ import type { Color, DragDropEvent } from './webview' */ function getCurrentWebviewWindow(): WebviewWindow { const webview = getCurrentWebview() - // @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor - return new WebviewWindow(webview.label, { skip: true }) + return new WebviewWindow(webview.label, { skip: true } as any) } /** @@ -36,9 +42,8 @@ async function getAllWebviewWindows(): Promise { windows.map( (w) => new WebviewWindow(w, { - // @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor skip: true - }) + } as any) ) ) } @@ -53,43 +58,24 @@ class WebviewWindow { // eslint-disable-next-line @typescript-eslint/no-explicit-any listeners: Record>> - /** - * Creates a new {@link Window} hosting a {@link Webview}. - * @example - * ```typescript - * import { WebviewWindow } from '@tauri-apps/api/webviewWindow' - * const webview = new WebviewWindow('my-label', { - * url: 'https://github.com/tauri-apps/tauri' - * }); - * webview.once('tauri://created', function () { - * // webview successfully created - * }); - * webview.once('tauri://error', function (e) { - * // an error happened creating the webview - * }); - * ``` - * - * @param label The unique webview label. Must be alphanumeric: `a-zA-Z-/:_`. - * @returns The {@link WebviewWindow} instance to communicate with the window and webview. - */ constructor( label: WebviewLabel, options: Omit - & WindowOptions = {} + & WindowOptions & { skip?: boolean } = {} ) { this.label = label - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment this.listeners = Object.create(null) - // @ts-expect-error `skip` is not a public API so it is not defined in WebviewOptions - if (!options?.skip) { + const internalOptions = options as InternalWebviewOptions + + if (!internalOptions?.skip) { invoke('plugin:webview|create_webview_window', { options: { - ...options, + ...internalOptions, parent: - typeof options.parent === 'string' - ? options.parent - : options.parent?.label, + typeof internalOptions.parent === 'string' + ? internalOptions.parent + : internalOptions.parent?.label, label } }) @@ -98,69 +84,32 @@ class WebviewWindow { } } - /** - * Gets the Webview for the webview associated with the given label. - * @example - * ```typescript - * import { Webview } from '@tauri-apps/api/webviewWindow'; - * const mainWebview = Webview.getByLabel('main'); - * ``` - * - * @param label The webview label. - * @returns The Webview instance to communicate with the webview or null if the webview doesn't exist. - */ static async getByLabel(label: string): Promise { const webview = (await getAllWebviewWindows()).find((w) => w.label === label) ?? null if (webview) { - // @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor - return new WebviewWindow(webview.label, { skip: true }) + return new WebviewWindow(webview.label, { skip: true } as any) } return null } - /** - * Get an instance of `Webview` for the current webview. - */ static getCurrent(): WebviewWindow { return getCurrentWebviewWindow() } - /** - * Gets a list of instances of `Webview` for all available webviews. - */ static async getAll(): Promise { return getAllWebviewWindows() } - /** - * Listen to an emitted event on this webivew window. - * - * @example - * ```typescript - * import { WebviewWindow } from '@tauri-apps/api/webviewWindow'; - * const unlisten = await WebviewWindow.getCurrent().listen('state-changed', (event) => { - * console.log(`Got error: ${payload}`); - * }); - * - * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted - * unlisten(); - * ``` - * - * @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`. - * @param handler Event handler. - * @returns A promise resolving to a function to unlisten to the event. - * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted. - */ async listen( event: EventName, handler: EventCallback ): Promise { if (this._handleTauriEvent(event, handler)) { return () => { - // eslint-disable-next-line security/detect-object-injection const listeners = this.listeners[event] - listeners.splice(listeners.indexOf(handler), 1) + const idx = listeners.indexOf(handler) + if (idx >= 0) listeners.splice(idx, 1) } } return listen(event, handler, { @@ -168,34 +117,15 @@ class WebviewWindow { }) } - /** - * Listen to an emitted event on this webview window only once. - * - * @example - * ```typescript - * import { WebviewWindow } from '@tauri-apps/api/webviewWindow'; - * const unlisten = await WebviewWindow.getCurrent().once('initialized', (event) => { - * console.log(`Webview initialized!`); - * }); - * - * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted - * unlisten(); - * ``` - * - * @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`. - * @param handler Event handler. - * @returns A promise resolving to a function to unlisten to the event. - * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted. - */ async once( event: EventName, handler: EventCallback ): Promise { if (this._handleTauriEvent(event, handler)) { return () => { - // eslint-disable-next-line security/detect-object-injection const listeners = this.listeners[event] - listeners.splice(listeners.indexOf(handler), 1) + const idx = listeners.indexOf(handler) + if (idx >= 0) listeners.splice(idx, 1) } } return once(event, handler, { @@ -206,27 +136,18 @@ class WebviewWindow { /** * Set the window and webview background color. * - * #### Platform-specific: - * - * - **Android / iOS:** Unsupported for the window layer. - * - **macOS / iOS**: Not implemented for the webview layer. - * - **Windows**: - * - alpha channel is ignored for the window layer. - * - On Windows 7, alpha channel is ignored for the webview layer. - * - On Windows 8 and newer, if alpha channel is not `0`, it will be ignored. - * - * @returns A promise indicating the success or failure of the operation. - * * @since 2.1.0 */ async setBackgroundColor(color: Color): Promise { - return invoke('plugin:window|set_background_color', { color }).then(() => { - return invoke('plugin:webview|set_webview_background_color', { color }) - }) + try { + await invoke('plugin:window|set_background_color', { color }) + await invoke('plugin:webview|set_webview_background_color', { color }) + } catch (err) { + throw new Error(`Failed to set background color: ${String(err)}`) + } } } -// Order matters, we use window APIs by default applyMixins(WebviewWindow, [Window, Webview]) /** Extends a base class by other specified classes, without overriding existing properties */ @@ -243,12 +164,12 @@ function applyMixins( typeof baseClass.prototype === 'object' && baseClass.prototype && name in baseClass.prototype - ) + ) { return + } Object.defineProperty( baseClass.prototype, name, - // eslint-disable-next-line Object.getOwnPropertyDescriptor(extendedClass.prototype, name) ?? Object.create(null) ) From e06aafbc51c2ff1e2e02a6bd590b4b340ef49fee Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Tue, 2 Sep 2025 16:08:17 +0530 Subject: [PATCH 2/7] Added back the documentation. --- packages/api/src/webviewWindow.ts | 100 ++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/packages/api/src/webviewWindow.ts b/packages/api/src/webviewWindow.ts index 546e9b74a9f4..e895c338a4bf 100644 --- a/packages/api/src/webviewWindow.ts +++ b/packages/api/src/webviewWindow.ts @@ -57,7 +57,25 @@ class WebviewWindow { /** Local event listeners. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any listeners: Record>> - + /** + * Creates a new {@link Window} hosting a {@link Webview}. + * @example + * ```typescript + * import { WebviewWindow } from '@tauri-apps/api/webviewWindow' + * const webview = new WebviewWindow('my-label', { + * url: 'https://github.com/tauri-apps/tauri' + * }); + * webview.once('tauri://created', function () { + * // webview successfully created + * }); + * webview.once('tauri://error', function (e) { + * // an error happened creating the webview + * }); + * ``` + * + * @param label The unique webview label. Must be alphanumeric: `a-zA-Z-/:_`. + * @returns The {@link WebviewWindow} instance to communicate with the window and webview. + */ constructor( label: WebviewLabel, options: Omit @@ -84,6 +102,18 @@ class WebviewWindow { } } + /** + * Gets the Webview for the webview associated with the given label. + * @example + * ```typescript + * import { Webview } from '@tauri-apps/api/webviewWindow'; + * const mainWebview = Webview.getByLabel('main'); + * ``` + * + * @param label The webview label. + * @returns The Webview instance to communicate with the webview or null if the webview doesn't exist. + */ + static async getByLabel(label: string): Promise { const webview = (await getAllWebviewWindows()).find((w) => w.label === label) ?? null @@ -93,6 +123,10 @@ class WebviewWindow { return null } + /** + * Get an instance of `Webview` for the current webview. + */ + static getCurrent(): WebviewWindow { return getCurrentWebviewWindow() } @@ -100,6 +134,25 @@ class WebviewWindow { static async getAll(): Promise { return getAllWebviewWindows() } + /** + * Listen to an emitted event on this webivew window. + * + * @example + * ```typescript + * import { WebviewWindow } from '@tauri-apps/api/webviewWindow'; + * const unlisten = await WebviewWindow.getCurrent().listen('state-changed', (event) => { + * console.log(`Got error: ${payload}`); + * }); + * + * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted + * unlisten(); + * ``` + * + * @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`. + * @param handler Event handler. + * @returns A promise resolving to a function to unlisten to the event. + * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted. + */ async listen( event: EventName, @@ -117,6 +170,26 @@ class WebviewWindow { }) } + /** + * Listen to an emitted event on this webview window only once. + * + * @example + * ```typescript + * import { WebviewWindow } from '@tauri-apps/api/webviewWindow'; + * const unlisten = await WebviewWindow.getCurrent().once('initialized', (event) => { + * console.log(`Webview initialized!`); + * }); + * + * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted + * unlisten(); + * ``` + * + * @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`. + * @param handler Event handler. + * @returns A promise resolving to a function to unlisten to the event. + * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted. + */ + async once( event: EventName, handler: EventCallback @@ -128,9 +201,28 @@ class WebviewWindow { if (idx >= 0) listeners.splice(idx, 1) } } - return once(event, handler, { - target: { kind: 'WebviewWindow', label: this.label } - }) + return once( + event, + handler, + /** + * Set the window and webview background color. + * + * #### Platform-specific: + * + * - **Android / iOS:** Unsupported for the window layer. + * - **macOS / iOS**: Not implemented for the webview layer. + * - **Windows**: + * - alpha channel is ignored for the window layer. + * - On Windows 7, alpha channel is ignored for the webview layer. + * - On Windows 8 and newer, if alpha channel is not `0`, it will be ignored. + * + * @returns A promise indicating the success or failure of the operation. + * + * @since 2.1.0 + */ { + target: { kind: 'WebviewWindow', label: this.label } + } + ) } /** From 2639f11bf22593d586ffecbd5390e82d724184d6 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Wed, 3 Sep 2025 14:49:12 +0530 Subject: [PATCH 3/7] Removed to strengthen type-safety --- packages/api/src/webviewWindow.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/api/src/webviewWindow.ts b/packages/api/src/webviewWindow.ts index e895c338a4bf..875c144e3af5 100644 --- a/packages/api/src/webviewWindow.ts +++ b/packages/api/src/webviewWindow.ts @@ -29,7 +29,7 @@ interface InternalWebviewOptions */ function getCurrentWebviewWindow(): WebviewWindow { const webview = getCurrentWebview() - return new WebviewWindow(webview.label, { skip: true } as any) + return new WebviewWindow(webview.label, { skip: true }) } /** @@ -43,7 +43,7 @@ async function getAllWebviewWindows(): Promise { (w) => new WebviewWindow(w, { skip: true - } as any) + }) ) ) } @@ -118,7 +118,7 @@ class WebviewWindow { const webview = (await getAllWebviewWindows()).find((w) => w.label === label) ?? null if (webview) { - return new WebviewWindow(webview.label, { skip: true } as any) + return new WebviewWindow(webview.label, { skip: true }) } return null } From 34f594f4d2d03a300f3b17cfa3cede0eddc5510e Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Sat, 20 Sep 2025 21:52:03 +0530 Subject: [PATCH 4/7] fix(api): address webviewWindow.ts review comments - Hide skip property from public API - Remove redundant parent property from InternalWebviewOptions - Restore getAllWebviewWindows doc comment - Remove misplaced setBackgroundColor documentation - Use error cause instead of string interpolation --- packages/api/package.json | 1 + packages/api/src/webviewWindow.ts | 52 ++++++++++--------------------- pnpm-lock.yaml | 3 ++ 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index 01fac97cc011..ace73991bc43 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -55,6 +55,7 @@ "eslint-plugin-security": "3.0.1", "fast-glob": "3.3.3", "globals": "^16.2.0", + "prettier": "^3.5.3", "rollup": "4.50.1", "tslib": "^2.8.1", "typescript": "^5.8.3", diff --git a/packages/api/src/webviewWindow.ts b/packages/api/src/webviewWindow.ts index 875c144e3af5..263cb9b36dbb 100644 --- a/packages/api/src/webviewWindow.ts +++ b/packages/api/src/webviewWindow.ts @@ -19,7 +19,6 @@ import type { Color, DragDropEvent } from './webview' interface InternalWebviewOptions extends Omit { skip?: boolean - parent?: Window | WebviewWindow | string } /** @@ -29,13 +28,11 @@ interface InternalWebviewOptions */ function getCurrentWebviewWindow(): WebviewWindow { const webview = getCurrentWebview() - return new WebviewWindow(webview.label, { skip: true }) + return new WebviewWindow(webview.label, { skip: true } as any) } /** - * Gets a list of instances of `Webview` for all available webview windows. - * - * @since 2.0.0 + * Gets a list of instances of `Webview` for all available webviews. */ async function getAllWebviewWindows(): Promise { return invoke('plugin:window|get_all_windows').then((windows) => @@ -43,7 +40,7 @@ async function getAllWebviewWindows(): Promise { (w) => new WebviewWindow(w, { skip: true - }) + } as any) ) ) } @@ -79,21 +76,23 @@ class WebviewWindow { constructor( label: WebviewLabel, options: Omit - & WindowOptions & { skip?: boolean } = {} + & WindowOptions = {} ) { this.label = label this.listeners = Object.create(null) - const internalOptions = options as InternalWebviewOptions + const internalOptions = options as InternalWebviewOptions & { + skip?: boolean + } if (!internalOptions?.skip) { invoke('plugin:webview|create_webview_window', { options: { ...internalOptions, parent: - typeof internalOptions.parent === 'string' - ? internalOptions.parent - : internalOptions.parent?.label, + typeof options.parent === 'string' + ? options.parent + : options.parent?.label, label } }) @@ -118,7 +117,7 @@ class WebviewWindow { const webview = (await getAllWebviewWindows()).find((w) => w.label === label) ?? null if (webview) { - return new WebviewWindow(webview.label, { skip: true }) + return new WebviewWindow(webview.label, { skip: true } as any) } return null } @@ -201,28 +200,9 @@ class WebviewWindow { if (idx >= 0) listeners.splice(idx, 1) } } - return once( - event, - handler, - /** - * Set the window and webview background color. - * - * #### Platform-specific: - * - * - **Android / iOS:** Unsupported for the window layer. - * - **macOS / iOS**: Not implemented for the webview layer. - * - **Windows**: - * - alpha channel is ignored for the window layer. - * - On Windows 7, alpha channel is ignored for the webview layer. - * - On Windows 8 and newer, if alpha channel is not `0`, it will be ignored. - * - * @returns A promise indicating the success or failure of the operation. - * - * @since 2.1.0 - */ { - target: { kind: 'WebviewWindow', label: this.label } - } - ) + return once(event, handler, { + target: { kind: 'WebviewWindow', label: this.label } + }) } /** @@ -235,7 +215,9 @@ class WebviewWindow { await invoke('plugin:window|set_background_color', { color }) await invoke('plugin:webview|set_webview_background_color', { color }) } catch (err) { - throw new Error(`Failed to set background color: ${String(err)}`) + const error = new Error('Failed to set background color') + ;(error as any).cause = err + throw error } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99e2ac6f9d45..a6616416f822 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,9 @@ importers: globals: specifier: ^16.2.0 version: 16.2.0 + prettier: + specifier: ^3.5.3 + version: 3.5.3 rollup: specifier: 4.50.1 version: 4.50.1 From f7d4387a7aa23bbed2e04edf428c83f51c476ed9 Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Wed, 24 Sep 2025 11:57:16 +0530 Subject: [PATCH 5/7] refactor: remove InternalWebviewOptions interface in webviewWindow.ts - Remove InternalWebviewOptions interface to simplify code - Replace with inline type checking using 'skip' in options - Add skip?: boolean to constructor parameter type - Remove unnecessary type assertions and @ts-expect-error comments --- packages/api/package.json | 1 - packages/api/src/webviewWindow.ts | 23 ++++++----------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index ace73991bc43..01fac97cc011 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -55,7 +55,6 @@ "eslint-plugin-security": "3.0.1", "fast-glob": "3.3.3", "globals": "^16.2.0", - "prettier": "^3.5.3", "rollup": "4.50.1", "tslib": "^2.8.1", "typescript": "^5.8.3", diff --git a/packages/api/src/webviewWindow.ts b/packages/api/src/webviewWindow.ts index 263cb9b36dbb..3937cd6fefc0 100644 --- a/packages/api/src/webviewWindow.ts +++ b/packages/api/src/webviewWindow.ts @@ -15,12 +15,6 @@ import type { EventName, EventCallback, UnlistenFn } from './event' import { invoke } from './core' import type { Color, DragDropEvent } from './webview' -// Internal-only type to avoid @ts-expect-error everywhere -interface InternalWebviewOptions - extends Omit { - skip?: boolean -} - /** * Get an instance of `Webview` for the current webview window. * @@ -28,7 +22,7 @@ interface InternalWebviewOptions */ function getCurrentWebviewWindow(): WebviewWindow { const webview = getCurrentWebview() - return new WebviewWindow(webview.label, { skip: true } as any) + return new WebviewWindow(webview.label, { skip: true }) } /** @@ -40,7 +34,7 @@ async function getAllWebviewWindows(): Promise { (w) => new WebviewWindow(w, { skip: true - } as any) + }) ) ) } @@ -76,19 +70,15 @@ class WebviewWindow { constructor( label: WebviewLabel, options: Omit - & WindowOptions = {} + & WindowOptions & { skip?: boolean } = {} ) { this.label = label this.listeners = Object.create(null) - const internalOptions = options as InternalWebviewOptions & { - skip?: boolean - } - - if (!internalOptions?.skip) { + if (!(options && 'skip' in options && options.skip)) { invoke('plugin:webview|create_webview_window', { options: { - ...internalOptions, + ...options, parent: typeof options.parent === 'string' ? options.parent @@ -117,7 +107,7 @@ class WebviewWindow { const webview = (await getAllWebviewWindows()).find((w) => w.label === label) ?? null if (webview) { - return new WebviewWindow(webview.label, { skip: true } as any) + return new WebviewWindow(webview.label, { skip: true }) } return null } @@ -125,7 +115,6 @@ class WebviewWindow { /** * Get an instance of `Webview` for the current webview. */ - static getCurrent(): WebviewWindow { return getCurrentWebviewWindow() } From 677a1d0a155cb76b5e800be95717ade9eade7b49 Mon Sep 17 00:00:00 2001 From: Kushal Meghani <168952248+KushalMeghani1644@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:33:18 +0530 Subject: [PATCH 6/7] Update packages/api/src/webviewWindow.ts Co-authored-by: Amr Bashir --- packages/api/src/webviewWindow.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/api/src/webviewWindow.ts b/packages/api/src/webviewWindow.ts index 3937cd6fefc0..5693de933639 100644 --- a/packages/api/src/webviewWindow.ts +++ b/packages/api/src/webviewWindow.ts @@ -204,9 +204,7 @@ class WebviewWindow { await invoke('plugin:window|set_background_color', { color }) await invoke('plugin:webview|set_webview_background_color', { color }) } catch (err) { - const error = new Error('Failed to set background color') - ;(error as any).cause = err - throw error + throw new Error('Failed to set background color', { cause: err }) } } } From 370ccd33070ca4d7f4bcea67cdc7b5c5456ea0bb Mon Sep 17 00:00:00 2001 From: KushalMeghani1644 Date: Thu, 25 Sep 2025 18:37:40 +0530 Subject: [PATCH 7/7] Update webviewWindow.ts, added comments. --- packages/api/src/webviewWindow.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/api/src/webviewWindow.ts b/packages/api/src/webviewWindow.ts index 5693de933639..d95ed9419d53 100644 --- a/packages/api/src/webviewWindow.ts +++ b/packages/api/src/webviewWindow.ts @@ -193,10 +193,20 @@ class WebviewWindow { target: { kind: 'WebviewWindow', label: this.label } }) } - /** * Set the window and webview background color. * + * #### Platform-specific: + * + * - **Android / iOS:** Unsupported for the window layer. + * - **macOS / iOS**: Not implemented for the webview layer. + * - **Windows**: + * - alpha channel is ignored for the window layer. + * - On Windows 7, alpha channel is ignored for the webview layer. + * - On Windows 8 and newer, if alpha channel is not `0`, it will be ignored. + * + * @returns A promise indicating the success or failure of the operation. + * * @since 2.1.0 */ async setBackgroundColor(color: Color): Promise { @@ -209,6 +219,8 @@ class WebviewWindow { } } + +// Order matters, we use window APIs by default applyMixins(WebviewWindow, [Window, Webview]) /** Extends a base class by other specified classes, without overriding existing properties */