diff --git a/docs/src/content/docs/learn/badges.mdx b/docs/src/content/docs/learn/badges.mdx deleted file mode 100644 index 3effaf7cc..000000000 --- a/docs/src/content/docs/learn/badges.mdx +++ /dev/null @@ -1,195 +0,0 @@ ---- -title: Badges ---- - -import { Tabs, TabItem } from "@astrojs/starlight/components"; - -## Introduction - -Wails provides a cross-platform badge service for desktop applications. This service allows you to display badges on your application tile or dock icon, which is useful for indicating unread messages, notifications, or other status information. - -## Basic Usage - -### Creating the Service - -First, initialize the badge service: - -```go -import "github.com/wailsapp/wails/v3/pkg/application" -import "github.com/wailsapp/wails/v3/pkg/services/badge" - -// Create a new badge service -badgeService := badge.New() - -// Register the service with the application -app := application.New(application.Options{ - Services: []application.Service{ - application.NewService(badgeService), - }, -}) -``` - -### Creating the Service with Custom Options (Windows Only) - -On Windows, you can customize the badge appearance with various options: - -```go -import "github.com/wailsapp/wails/v3/pkg/application" -import "github.com/wailsapp/wails/v3/pkg/services/badge" -import "image/color" - -// Create a badge service with custom options -options := badge.Options{ - TextColour: color.RGBA{255, 255, 255, 255}, // White text - BackgroundColour: color.RGBA{0, 0, 255, 255}, // Blue background - FontName: "consolab.ttf", // Bold Consolas font - FontSize: 20, // Font size for single character - SmallFontSize: 14, // Font size for multiple characters -} - -badgeService := badge.NewWithOptions(options) - -// Register the service with the application -app := application.New(application.Options{ - Services: []application.Service{ - application.NewService(badgeService), - }, -}) -``` - -## Badge Operations - -### Setting a Badge - -Set a badge on the application tile/dock icon: - -```go -// Set a default badge -badgeService.SetBadge("") - -// Set a numeric badge -badgeService.SetBadge("3") - -// Set a text badge -badgeService.SetBadge("New") -``` - -### Setting a Custom Badge - -Set a badge on the application tile/dock icon with one-off options applied: - -#### Go -```go -options := badge.Options{ - BackgroundColour: color.RGBA{0, 255, 255, 255}, - FontName: "arialb.ttf", // System font - FontSize: 16, - SmallFontSize: 10, - TextColour: color.RGBA{0, 0, 0, 255}, -} - -// Set a default badge -badgeService.SetCustomBadge("", options) - -// Set a numeric badge -badgeService.SetCustomBadge("3", options) - -// Set a text badge -badgeService.SetCustomBadge("New", options) -``` - -### Removing a Badge - -Remove the badge from the application icon: - -```go -badgeService.RemoveBadge() -``` - -## Platform Considerations - - - - - On macOS, badges: - - - Are displayed directly on the dock icon - - Support text values - - Automatically handle dark/light mode appearance - - Use the standard macOS dock badge styling - - Automatically handle label overflow - - Do not support customization options (any options passed to NewWithOptions will be ignored) - - Will display "●" as the default badge if an empty label is provided - - - - - - On Windows, badges: - - - Are displayed as an overlay icon in the taskbar - - Support text values - - Allow customization of colors, font, and font sizes - - Adapt to Windows theme settings - - Require the application to have a window - - Use smaller font size automatically for badges with multiple characters - - Do not handle label overflow - - Support the following customization options: - - TextColour: Text color (default: white) - - BackgroundColour: Badge background color (default: red) - - FontName: Font file name (default: "segoeuib.ttf") - - FontSize: Font size for single character (default: 18) - - SmallFontSize: Font size for multiple characters (default: 14) - - - - - - On Linux: - - - Badge functionality is not available - - - - -## Best Practices - -1. Use badges sparingly: - - Too many badge updates can distract users - - Reserve badges for important notifications - -2. Keep badge text short: - - Numeric badges are most effective - - On macOS, text badges should be brief - -3. For Windows customization: - - Ensure high contrast between text and background colors - - Test with different text lengths as font size decreases with length - - Use common system fonts to ensure availability - -## API Reference - -### Service Management -| Method | Description | -|--------------------------------------------|-------------------------------------------------------| -| `New()` | Creates a new badge service with default options | -| `NewWithOptions(options Options)` | Creates a new badge service with custom options (Windows only, options are ignored on macOS) | - -### Badge Operations -| Method | Description | -|----------------------------------------------|------------------------------------------------------------| -| `SetBadge(label string) error` | Sets a badge with the specified label | -| `SetCustomBadge(label string, options Options) error` | Sets a badge with the specified label and custom styling options (Windows only) | -| `RemoveBadge() error` | Removes the badge from the application icon | -### Structs and Types - -```go -// Options for customizing badge appearance (Windows only) -type Options struct { - TextColour color.RGBA // Color of the badge text - BackgroundColour color.RGBA // Color of the badge background - FontName string // Font file name (e.g., "segoeuib.ttf") - FontSize int // Font size for single character - SmallFontSize int // Font size for multiple characters -} -``` diff --git a/docs/src/content/docs/learn/dock.mdx b/docs/src/content/docs/learn/dock.mdx new file mode 100644 index 000000000..76f385726 --- /dev/null +++ b/docs/src/content/docs/learn/dock.mdx @@ -0,0 +1,232 @@ +--- +title: Dock +--- + +import { Tabs, TabItem } from "@astrojs/starlight/components"; + +## Introduction + +Wails provides a cross-platform Dock service for desktop applications. This service allows you to: + +- Hide and show the application icon in the macOS Dock +- Display badges on your application tile or dock/taskbar icon (macOS and Windows) + +## Basic Usage + +### Creating the Service + +First, initialize the dock service: + +```go +import "github.com/wailsapp/wails/v3/pkg/application" +import "github.com/wailsapp/wails/v3/pkg/services/dock" + +// Create a new Dock service +dockService := dock.New() + +// Register the service with the application +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(dockService), + }, +}) +``` + +### Creating the Service with Custom Badge Options (Windows Only) + +On Windows, you can customize the badge appearance with various options: + +```go +import "github.com/wailsapp/wails/v3/pkg/application" +import "github.com/wailsapp/wails/v3/pkg/services/dock" +import "image/color" + +// Create a dock service with custom badge options +options := dock.BadgeOptions{ + TextColour: color.RGBA{255, 255, 255, 255}, // White text + BackgroundColour: color.RGBA{0, 0, 255, 255}, // Blue background + FontName: "consolab.ttf", // Bold Consolas font + FontSize: 20, // Font size for single character + SmallFontSize: 14, // Font size for multiple characters +} + +dockService := dock.NewWithOptions(options) + +// Register the service with the application +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(dockService), + }, +}) +``` + +## Dock Operations + +### Hiding the dock app icon + +Hide the app icon from the macOS Dock: + +```go +// Hide the app icon +dockService.HideAppIcon() +``` + +### Showing the dock app icon + +Show the app icon in the macOS Dock: + +```go +// Show the app icon +dockService.ShowAppIcon() +``` + +## Badge Operations + +### Setting a Badge + +Set a badge on the application tile/dock icon: + +```go +// Set a default badge +dockService.SetBadge("") + +// Set a numeric badge +dockService.SetBadge("3") + +// Set a text badge +dockService.SetBadge("New") +``` + +### Setting a Custom Badge (Windows Only) + +Set a badge with one-off options applied: + +```go +options := dock.BadgeOptions{ + BackgroundColour: color.RGBA{0, 255, 255, 255}, + FontName: "arialb.ttf", // System font + FontSize: 16, + SmallFontSize: 10, + TextColour: color.RGBA{0, 0, 0, 255}, +} + +// Set a default badge +dockService.SetCustomBadge("", options) + +// Set a numeric badge +dockService.SetCustomBadge("3", options) + +// Set a text badge +dockService.SetCustomBadge("New", options) +``` + +### Removing a Badge + +Remove the badge from the application icon: + +```go +dockService.RemoveBadge() +``` + +## Platform Considerations + + + + + On macOS: + + - The dock icon can be **hidden** and **shown** + - Badges are displayed directly on the dock icon + - Badge options are **not customizable** (any options passed to `NewWithOptions`/`SetCustomBadge` are ignored) + - The standard macOS dock badge styling is used and automatically adapts to appearance + - Label overflow is handled by the system + - Providing an empty label displays a default badge of "●" + + + + + + On Windows: + + - Hiding/showing the taskbar icon is not currently supported by this service + - Badges are displayed as an overlay icon in the taskbar + - Badges support text values + - Badge appearance can be customized via `BadgeOptions` + - The application must have a window for badges to display + - A smaller font size is automatically used for multi-character labels + - Label overflow is not handled + - Customization options: + - **TextColour**: Text color (default: white) + - **BackgroundColour**: Badge background color (default: red) + - **FontName**: Font file name (default: "segoeuib.ttf") + - **FontSize**: Font size for single character (default: 18) + - **SmallFontSize**: Font size for multiple characters (default: 14) + + + + + + On Linux: + + - Dock icon visibility and badge functionality are not available + + + + +## Best Practices + +1. **When hiding the dock icon (macOS):** + - Ensure users can still access your app (e.g., via [system tray](https://v3alpha.wails.io/learn/systray/)) + - Include a "Quit" option in your alternative UI + - The app won't appear in Command+Tab switcher + - Open windows remain visible and functional + - Closing all windows may not quit the app (macOS behavior varies) + - Users lose the standard way to quit via Dock right-click + +2. **Use badges sparingly:** + - Too many badge updates can distract users + - Reserve badges for important notifications + +3. **Keep badge text short:** + - Numeric badges are most effective + - On macOS, text badges should be brief + +4. **For Windows badge customization:** + - Ensure high contrast between text and background colors + - Test with different text lengths as font size decreases with length + - Use common system fonts to ensure availability + + +## API Reference + +### Service Management +| Method | Description | +|--------------------------------------------|-------------------------------------------------------| +| `New()` | Creates a new dock service | +| `NewWithOptions(options BadgeOptions)` | Creates a new dock service with custom badge options (Windows only; options are ignored on macOS and Linux) | + +### Dock Operations +| Method | Description | +|--------------------------------|-------------------------------------------------------------| +| `HideAppIcon()` | Hides the app icon from the macOS Dock (macOS only) | +| `ShowAppIcon()` | Shows the app icon in the macOS Dock (macOS only) | + +### Badge Operations +| Method | Description | +|---------------------------------------------------|------------------------------------------------------------| +| `SetBadge(label string) error` | Sets a badge with the specified label | +| `SetCustomBadge(label string, options BadgeOptions) error` | Sets a badge with the specified label and custom styling options (Windows only) | +| `RemoveBadge() error` | Removes the badge from the application icon | + +### Structs and Types + +```go +// Options for customizing badge appearance (Windows only) +type BadgeOptions struct { + TextColour color.RGBA // Color of the badge text + BackgroundColour color.RGBA // Color of the badge background + FontName string // Font file name (e.g., "segoeuib.ttf") + FontSize int // Font size for single character + SmallFontSize int // Font size for multiple characters +} +``` diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index f49f62cf9..f0013f982 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -17,6 +17,7 @@ After processing, the content will be moved to the main changelog and this file ## Added +- Add macOS Dock service to hide/show app icon in the dock @popaprozac in [PR](https://github.com/wailsapp/wails/pull/4451) ## Changed diff --git a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts deleted file mode 100644 index f959d50dc..000000000 --- a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -/** - * Service represents the notifications service - * @module - */ - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import * as $models from "./models.js"; - -/** - * RemoveBadge removes the badge label from the application icon. - */ -export function RemoveBadge(): $CancellablePromise { - return $Call.ByID(2374916939); -} - -/** - * SetBadge sets the badge label on the application icon. - */ -export function SetBadge(label: string): $CancellablePromise { - return $Call.ByID(784276339, label); -} - -export function SetCustomBadge(label: string, options: $models.Options): $CancellablePromise { - return $Call.ByID(3058653106, label, options); -} diff --git a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts new file mode 100644 index 000000000..abf1f22e4 --- /dev/null +++ b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts @@ -0,0 +1,53 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service represents the dock service + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * HideAppIcon hides the app icon in the dock/taskbar. + */ +export function HideAppIcon(): $CancellablePromise { + return $Call.ByID(3413658144); +} + +/** + * RemoveBadge removes the badge label from the application icon. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function RemoveBadge(): $CancellablePromise { + return $Call.ByID(2752757297); +} + +/** + * SetBadge sets the badge label on the application icon. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function SetBadge(label: string): $CancellablePromise { + return $Call.ByID(1717705661, label); +} + +/** + * SetCustomBadge sets the badge label on the application icon with custom options. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function SetCustomBadge(label: string, options: $models.BadgeOptions): $CancellablePromise { + return $Call.ByID(2730169760, label, options); +} + +/** + * ShowAppIcon shows the app icon in the dock/taskbar. + */ +export function ShowAppIcon(): $CancellablePromise { + return $Call.ByID(3409697379); +} diff --git a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts similarity index 65% rename from v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts rename to v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts index df3ea9723..fbdaf19f3 100644 --- a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts +++ b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts @@ -1,11 +1,11 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -import * as BadgeService from "./badgeservice.js"; +import * as DockService from "./dockservice.js"; export { - BadgeService + DockService }; export { - Options + BadgeOptions } from "./models.js"; diff --git a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts similarity index 80% rename from v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts rename to v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts index 67ed264c0..f97c8a4c6 100644 --- a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts +++ b/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts @@ -9,15 +9,18 @@ import { Create as $Create } from "@wailsio/runtime"; // @ts-ignore: Unused imports import * as color$0 from "../../../../../../../image/color/models.js"; -export class Options { +/** + * BadgeOptions represents options for customizing badge appearance + */ +export class BadgeOptions { "TextColour": color$0.RGBA; "BackgroundColour": color$0.RGBA; "FontName": string; "FontSize": number; "SmallFontSize": number; - /** Creates a new Options instance. */ - constructor($$source: Partial = {}) { + /** Creates a new BadgeOptions instance. */ + constructor($$source: Partial = {}) { if (!("TextColour" in $$source)) { this["TextColour"] = (new color$0.RGBA()); } @@ -38,9 +41,9 @@ export class Options { } /** - * Creates a new Options instance from a string or object. + * Creates a new BadgeOptions instance from a string or object. */ - static createFrom($$source: any = {}): Options { + static createFrom($$source: any = {}): BadgeOptions { const $$createField0_0 = $$createType0; const $$createField1_0 = $$createType0; let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; @@ -50,7 +53,7 @@ export class Options { if ("BackgroundColour" in $$parsedSource) { $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]); } - return new Options($$parsedSource as Partial); + return new BadgeOptions($$parsedSource as Partial); } } diff --git a/v3/examples/badge-custom/frontend/dist/assets/index--TgUfkZn.js b/v3/examples/badge-custom/frontend/dist/assets/index--TgUfkZn.js deleted file mode 100644 index 6d342144b..000000000 --- a/v3/examples/badge-custom/frontend/dist/assets/index--TgUfkZn.js +++ /dev/null @@ -1,6 +0,0 @@ -var ue=Object.defineProperty;var de=(t,e,n)=>e in t?ue(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var j=(t,e,n)=>de(t,typeof e!="symbol"?e+"":e,n);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(o){if(o.ep)return;o.ep=!0;const i=n(o);fetch(o.href,i)}})();const fe="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function te(t=21){let e="",n=t|0;for(;n--;)e+=fe[Math.random()*64|0];return e}const we=window.location.origin+"/wails/runtime",z=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let pe=te();function R(t,e=""){return function(n,r=null){return me(t,n,e,r)}}async function me(t,e,n,r){var o,i;let s=new URL(we);s.searchParams.append("object",t.toString()),s.searchParams.append("method",e.toString()),r&&s.searchParams.append("args",JSON.stringify(r));let c={"x-wails-client-id":pe};n&&(c["x-wails-window-name"]=n);let l=await fetch(s,{headers:c});if(!l.ok)throw new Error(await l.text());return((i=(o=l.headers.get("Content-Type"))===null||o===void 0?void 0:o.indexOf("application/json"))!==null&&i!==void 0?i:-1)!==-1?l.json():l.text()}R(z.System);const P=function(){var t,e,n,r,o;try{if(!((e=(t=window.chrome)===null||t===void 0?void 0:t.webview)===null||e===void 0)&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((o=(r=(n=window.webkit)===null||n===void 0?void 0:n.messageHandlers)===null||r===void 0?void 0:r.external)===null||o===void 0)&&o.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(` -%c⚠️ Browser Environment Detected %c - -%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. -More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development -`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function k(t){P==null||P(t)}function ne(){return window._wails.environment.OS==="windows"}function he(){return!!window._wails.environment.Debug}function ge(){return new MouseEvent("mousedown").buttons===0}function re(t){var e;return t.target instanceof HTMLElement?t.target:!(t.target instanceof HTMLElement)&&t.target instanceof Node&&(e=t.target.parentElement)!==null&&e!==void 0?e:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",Ee);const ye=R(z.ContextMenu),be=0;function ve(t,e,n,r){ye(be,{id:t,x:e,y:n,data:r})}function Ee(t){const e=re(t),n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(n){t.preventDefault();const r=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");ve(n,t.clientX,t.clientY,r)}else je(t,e)}function je(t,e){if(he())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return}if(e.isContentEditable)return;const n=window.getSelection(),r=n&&n.toString().length>0;if(r)for(let o=0;o{U=t,U||(v=E=!1,u())};window.addEventListener("mousedown",X,{capture:!0});window.addEventListener("mousemove",X,{capture:!0});window.addEventListener("mouseup",X,{capture:!0});for(const t of["click","contextmenu","dblclick"])window.addEventListener(t,Se,{capture:!0});function Se(t){(C||E)&&(t.stopImmediatePropagation(),t.stopPropagation(),t.preventDefault())}const F=0,Ce=1,I=2;function X(t){let e,n=t.buttons;switch(t.type){case"mousedown":e=F,D||(n=h|1<"u"||typeof e=="object"))try{var n=L.call(e);return(n===Te||n===Ae||n===Pe||n===Le)&&e("")==null}catch{}return!1})}function Ie(t){if(J(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;try{y(t,null,N)}catch(e){if(e!==O)return!1}return!Y(t)&&W(t)}function _e(t){if(J(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;if(He)return W(t);if(Y(t))return!1;var e=L.call(t);return e!==ke&&e!==Be&&!/^\[object HTML/.test(e)?!1:W(t)}const m=y?Ie:_e;var _;class G extends Error{constructor(e,n){super(e,n),this.name="CancelError"}}class x extends Error{constructor(e,n,r){super((r??"Unhandled rejection in cancelled promise.")+" Reason: "+Ue(n),{cause:n}),this.promise=e,this.name="CancelledRejectionError"}}const f=Symbol("barrier"),Q=Symbol("cancelImpl"),Z=(_=Symbol.species)!==null&&_!==void 0?_:Symbol("speciesPolyfill");class a extends Promise{constructor(e,n){let r,o;if(super((l,d)=>{r=l,o=d}),this.constructor[Z]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let i={promise:this,resolve:r,reject:o,get oncancelled(){return n??null},set oncancelled(l){n=l??void 0}};const s={get root(){return s},resolving:!1,settled:!1};Object.defineProperties(this,{[f]:{configurable:!1,enumerable:!1,writable:!0,value:null},[Q]:{configurable:!1,enumerable:!1,writable:!1,value:ie(i,s)}});const c=le(i,s);try{e(se(i,s),c)}catch(l){s.resolving?console.log("Unhandled exception in CancellablePromise executor.",l):c(l)}}cancel(e){return new a(n=>{Promise.all([this[Q](new G("Promise cancelled.",{cause:e})),Ne(this)]).then(()=>n(),()=>n())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,n,r){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(m(e)||(e=$),m(n)||(n=ee),e===$&&n==ee)return new a(i=>i(this));const o={};return this[f]=o,new a((i,s)=>{super.then(c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(e(c))}catch(d){s(d)}},c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(n(c))}catch(d){s(d)}})},async i=>{try{return r==null?void 0:r(i)}finally{await this.cancel(i)}})}catch(e,n){return this.then(void 0,e,n)}finally(e,n){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return m(e)?this.then(r=>a.resolve(e()).then(()=>r),r=>a.resolve(e()).then(()=>{throw r}),n):this.then(e,e,n)}static get[Z](){return Promise}static all(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.all(n).then(o,i)},o=>M(r,n,o));return r}static allSettled(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.allSettled(n).then(o,i)},o=>M(r,n,o));return r}static any(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.any(n).then(o,i)},o=>M(r,n,o));return r}static race(e){let n=Array.from(e);const r=new a((o,i)=>{Promise.race(n).then(o,i)},o=>M(r,n,o));return r}static cancel(e){const n=new a(()=>{});return n.cancel(e),n}static timeout(e,n){const r=new a(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void r.cancel(n)):setTimeout(()=>void r.cancel(n),e),r}static sleep(e,n){return new a(r=>{setTimeout(()=>r(n),e)})}static reject(e){return new a((n,r)=>r(e))}static resolve(e){return e instanceof a?e:new a(n=>n(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new a((n,r)=>{e.resolve=n,e.reject=r},n=>{var r;(r=e.oncancelled)===null||r===void 0||r.call(e,n)}),e}}function ie(t,e){let n;return r=>{if(e.settled||(e.settled=!0,e.reason=r,t.reject(r),Promise.prototype.then.call(t.promise,void 0,o=>{if(o!==r)throw o})),!(!e.reason||!t.oncancelled))return n=new Promise(o=>{try{o(t.oncancelled(e.reason.cause))}catch(i){Promise.reject(new x(t.promise,i,"Unhandled exception in oncancelled callback."))}}).catch(o=>{Promise.reject(new x(t.promise,o,"Unhandled rejection in oncancelled callback."))}),t.oncancelled=null,n}}function se(t,e){return n=>{if(!e.resolving){if(e.resolving=!0,n===t.promise){if(e.settled)return;e.settled=!0,t.reject(new TypeError("A promise cannot be resolved with itself."));return}if(n!=null&&(typeof n=="object"||typeof n=="function")){let r;try{r=n.then}catch(o){e.settled=!0,t.reject(o);return}if(m(r)){try{let s=n.cancel;if(m(s)){const c=l=>{Reflect.apply(s,n,[l])};e.reason?ie(Object.assign(Object.assign({},t),{oncancelled:c}),e)(e.reason):t.oncancelled=c}}catch{}const o={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(s){this.root.settled=s},get reason(){return this.root.reason}},i=le(t,o);try{Reflect.apply(r,n,[se(t,o),i])}catch(s){i(s)}return}}e.settled||(e.settled=!0,t.resolve(n))}}}function le(t,e){return n=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(n instanceof G&&e.reason instanceof G&&Object.is(n.cause,e.reason.cause))return}catch{}Promise.reject(new x(t.promise,n))}else e.settled=!0,t.reject(n)}}function M(t,e,n){const r=[];for(const o of e){let i;try{if(!m(o.then)||(i=o.cancel,!m(i)))continue}catch{continue}let s;try{s=Reflect.apply(i,o,[n])}catch(c){Promise.reject(new x(t,c,"Unhandled exception in cancel method."));continue}s&&r.push((s instanceof Promise?s:Promise.resolve(s)).catch(c=>{Promise.reject(new x(t,c,"Unhandled rejection in cancel method."))}))}return Promise.all(r)}function $(t){return t}function ee(t){throw t}function Ue(t){try{if(t instanceof Error||typeof t!="object"||t.toString!==Object.prototype.toString)return""+t}catch{}try{return JSON.stringify(t)}catch{}try{return Object.prototype.toString.call(t)}catch{}return""}function Ne(t){var e;let n=(e=t[f])!==null&&e!==void 0?e:{};return"promise"in n||Object.assign(n,g()),t[f]==null&&(n.resolve(),t[f]=n),n.promise}let g=Promise.withResolvers;g&&typeof g=="function"?g=g.bind(Promise):g=function(){let t,e;return{promise:new Promise((r,o)=>{t=r,e=o}),resolve:t,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Ve;window._wails.callErrorHandler=qe;const We=R(z.Call),Ge=R(z.CancelCall),b=new Map,Xe=0,Ye=0;class Je extends Error{constructor(e,n){super(e,n),this.name="RuntimeError"}}function Ve(t,e,n){const r=ce(t);if(r)if(!e)r.resolve(void 0);else if(!n)r.resolve(e);else try{r.resolve(JSON.parse(e))}catch(o){r.reject(new TypeError("could not parse result: "+o.message,{cause:o}))}}function qe(t,e,n){const r=ce(t);if(r)if(!n)r.reject(new Error(e));else{let o;try{o=JSON.parse(e)}catch(c){r.reject(new TypeError("could not parse error: "+c.message,{cause:c}));return}let i={};o.cause&&(i.cause=o.cause);let s;switch(o.kind){case"ReferenceError":s=new ReferenceError(o.message,i);break;case"TypeError":s=new TypeError(o.message,i);break;case"RuntimeError":s=new Je(o.message,i);break;default:s=new Error(o.message,i);break}r.reject(s)}}function ce(t){const e=b.get(t);return b.delete(t),e}function Ke(){let t;do t=te();while(b.has(t));return t}function Qe(t){const e=Ke(),n=a.withResolvers();b.set(e,{resolve:n.resolve,reject:n.reject});const r=We(Xe,Object.assign({"call-id":e},t));let o=!1;r.then(()=>{o=!0},s=>{b.delete(e),n.reject(s)});const i=()=>(b.delete(e),Ge(Ye,{"call-id":e}).catch(s=>{console.error("Error while requesting binding call cancellation:",s)}));return n.oncancelled=()=>o?i():r.then(i),n.promise}function V(t,...e){return Qe({methodID:t,args:e})}const w=new Map;class Ze{constructor(e,n,r){this.eventName=e,this.callback=n,this.maxCallbacks=r||-1}dispatch(e){try{this.callback(e)}catch(n){console.error(n)}return this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0)}}function $e(t){let e=w.get(t.eventName);e&&(e=e.filter(n=>n!==t),e.length===0?w.delete(t.eventName):w.set(t.eventName,e))}window._wails=window._wails||{};window._wails.dispatchWailsEvent=rt;const et=R(z.Events),tt=0;class nt{constructor(e,n=null){this.name=e,this.data=n}}function rt(t){let e=w.get(t.name);if(!e)return;let n=new nt(t.name,t.data);"sender"in t&&(n.sender=t.sender),e=e.filter(r=>!r.dispatch(n)),e.length===0?w.delete(t.name):w.set(t.name,e)}function ot(t,e,n){let r=w.get(t)||[];const o=new Ze(t,e,n);return r.push(o),w.set(t,r),()=>$e(o)}function it(t,e){return ot(t,e,-1)}function ae(t){return et(tt,t)}window._wails=window._wails||{};window._wails.invoke=k;k("wails:runtime:ready");function st(){return V(2374916939)}function lt(t){return V(784276339,t)}function ct(t,e){return V(3058653106,t,e)}class B{constructor(e={}){j(this,"R");j(this,"G");j(this,"B");j(this,"A");"R"in e||(this.R=0),"G"in e||(this.G=0),"B"in e||(this.B=0),"A"in e||(this.A=0),Object.assign(this,e)}static createFrom(e={}){let n=typeof e=="string"?JSON.parse(e):e;return new B(n)}}const at=document.getElementById("set-custom"),ut=document.getElementById("set"),dt=document.getElementById("remove"),ft=document.getElementById("set-go"),wt=document.getElementById("remove-go"),q=document.getElementById("label"),pt=document.getElementById("time");at.addEventListener("click",()=>{console.log("click!");let t=q.value;ct(t,{BackgroundColour:B.createFrom({R:0,G:255,B:255,A:255}),FontName:"arialb.ttf",FontSize:16,SmallFontSize:10,TextColour:B.createFrom({R:0,G:0,B:0,A:255})})});ut.addEventListener("click",()=>{let t=q.value;lt(t)});dt.addEventListener("click",()=>{st()});ft.addEventListener("click",()=>{let t=q.value;ae({name:"set:badge",data:t})});wt.addEventListener("click",()=>{ae({name:"remove:badge",data:null})});it("time",t=>{pt.innerText=t.data}); diff --git a/v3/examples/badge-custom/frontend/dist/assets/index-DOJs-2ns.js b/v3/examples/badge-custom/frontend/dist/assets/index-DOJs-2ns.js new file mode 100644 index 000000000..780f115b6 --- /dev/null +++ b/v3/examples/badge-custom/frontend/dist/assets/index-DOJs-2ns.js @@ -0,0 +1,1415 @@ +var __defProp = Object.defineProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); +(function polyfill() { + const relList = document.createElement("link").relList; + if (relList && relList.supports && relList.supports("modulepreload")) { + return; + } + for (const link of document.querySelectorAll('link[rel="modulepreload"]')) { + processPreload(link); + } + new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.type !== "childList") { + continue; + } + for (const node of mutation.addedNodes) { + if (node.tagName === "LINK" && node.rel === "modulepreload") + processPreload(node); + } + } + }).observe(document, { childList: true, subtree: true }); + function getFetchOpts(link) { + const fetchOpts = {}; + if (link.integrity) fetchOpts.integrity = link.integrity; + if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy; + if (link.crossOrigin === "use-credentials") + fetchOpts.credentials = "include"; + else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit"; + else fetchOpts.credentials = "same-origin"; + return fetchOpts; + } + function processPreload(link) { + if (link.ep) + return; + link.ep = true; + const fetchOpts = getFetchOpts(link); + fetch(link.href, fetchOpts); + } +})(); +const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"; +function nanoid(size = 21) { + let id = ""; + let i = size | 0; + while (i--) { + id += urlAlphabet[Math.random() * 64 | 0]; + } + return id; +} +const runtimeURL = window.location.origin + "/wails/runtime"; +const objectNames = Object.freeze({ + Call: 0, + Clipboard: 1, + Application: 2, + Events: 3, + ContextMenu: 4, + Dialog: 5, + Window: 6, + Screens: 7, + System: 8, + Browser: 9, + CancelCall: 10 +}); +let clientId = nanoid(); +function newRuntimeCaller(object, windowName = "") { + return function(method, args = null) { + return runtimeCallWithID(object, method, windowName, args); + }; +} +async function runtimeCallWithID(objectID, method, windowName, args) { + var _a2, _b; + let url = new URL(runtimeURL); + url.searchParams.append("object", objectID.toString()); + url.searchParams.append("method", method.toString()); + if (args) { + url.searchParams.append("args", JSON.stringify(args)); + } + let headers = { + ["x-wails-client-id"]: clientId + }; + if (windowName) { + headers["x-wails-window-name"] = windowName; + } + let response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(await response.text()); + } + if (((_b = (_a2 = response.headers.get("Content-Type")) === null || _a2 === void 0 ? void 0 : _a2.indexOf("application/json")) !== null && _b !== void 0 ? _b : -1) !== -1) { + return response.json(); + } else { + return response.text(); + } +} +newRuntimeCaller(objectNames.System); +const _invoke = function() { + var _a2, _b, _c, _d, _e; + try { + if ((_b = (_a2 = window.chrome) === null || _a2 === void 0 ? void 0 : _a2.webview) === null || _b === void 0 ? void 0 : _b.postMessage) { + return window.chrome.webview.postMessage.bind(window.chrome.webview); + } else if ((_e = (_d = (_c = window.webkit) === null || _c === void 0 ? void 0 : _c.messageHandlers) === null || _d === void 0 ? void 0 : _d["external"]) === null || _e === void 0 ? void 0 : _e.postMessage) { + return window.webkit.messageHandlers["external"].postMessage.bind(window.webkit.messageHandlers["external"]); + } + } catch (e) { + } + console.warn("\n%c⚠️ Browser Environment Detected %c\n\n%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode.\nMore information at: https://v3.wails.io/learn/build/#using-a-browser-for-development\n", "background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;", "background: transparent;", "color: #ffffff; font-style: italic; font-weight: bold;"); + return null; +}(); +function invoke(msg) { + _invoke === null || _invoke === void 0 ? void 0 : _invoke(msg); +} +function IsWindows() { + return window._wails.environment.OS === "windows"; +} +function IsDebug() { + return Boolean(window._wails.environment.Debug); +} +function canTrackButtons() { + return new MouseEvent("mousedown").buttons === 0; +} +function eventTarget(event) { + var _a2; + if (event.target instanceof HTMLElement) { + return event.target; + } else if (!(event.target instanceof HTMLElement) && event.target instanceof Node) { + return (_a2 = event.target.parentElement) !== null && _a2 !== void 0 ? _a2 : document.body; + } else { + return document.body; + } +} +document.addEventListener("DOMContentLoaded", () => { +}); +window.addEventListener("contextmenu", contextMenuHandler); +const call$2 = newRuntimeCaller(objectNames.ContextMenu); +const ContextMenuOpen = 0; +function openContextMenu(id, x, y, data) { + void call$2(ContextMenuOpen, { id, x, y, data }); +} +function contextMenuHandler(event) { + const target = eventTarget(event); + const customContextMenu = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu").trim(); + if (customContextMenu) { + event.preventDefault(); + const data = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu-data"); + openContextMenu(customContextMenu, event.clientX, event.clientY, data); + } else { + processDefaultContextMenu(event, target); + } +} +function processDefaultContextMenu(event, target) { + if (IsDebug()) { + return; + } + switch (window.getComputedStyle(target).getPropertyValue("--default-contextmenu").trim()) { + case "show": + return; + case "hide": + event.preventDefault(); + return; + } + if (target.isContentEditable) { + return; + } + const selection = window.getSelection(); + const hasSelection = selection && selection.toString().length > 0; + if (hasSelection) { + for (let i = 0; i < selection.rangeCount; i++) { + const range = selection.getRangeAt(i); + const rects = range.getClientRects(); + for (let j = 0; j < rects.length; j++) { + const rect = rects[j]; + if (document.elementFromPoint(rect.left, rect.top) === target) { + return; + } + } + } + } + if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { + if (hasSelection || !target.readOnly && !target.disabled) { + return; + } + } + event.preventDefault(); +} +function GetFlag(key) { + try { + return window._wails.flags[key]; + } catch (e) { + throw new Error("Unable to retrieve flag '" + key + "': " + e, { cause: e }); + } +} +let canDrag = false; +let dragging = false; +let resizable = false; +let canResize = false; +let resizing = false; +let resizeEdge = ""; +let defaultCursor = "auto"; +let buttons = 0; +const buttonsTracked = canTrackButtons(); +window._wails = window._wails || {}; +window._wails.setResizable = (value) => { + resizable = value; + if (!resizable) { + canResize = resizing = false; + setResize(); + } +}; +window.addEventListener("mousedown", update, { capture: true }); +window.addEventListener("mousemove", update, { capture: true }); +window.addEventListener("mouseup", update, { capture: true }); +for (const ev of ["click", "contextmenu", "dblclick"]) { + window.addEventListener(ev, suppressEvent, { capture: true }); +} +function suppressEvent(event) { + if (dragging || resizing) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } +} +const MouseDown = 0; +const MouseUp = 1; +const MouseMove = 2; +function update(event) { + let eventType, eventButtons = event.buttons; + switch (event.type) { + case "mousedown": + eventType = MouseDown; + if (!buttonsTracked) { + eventButtons = buttons | 1 << event.button; + } + break; + case "mouseup": + eventType = MouseUp; + if (!buttonsTracked) { + eventButtons = buttons & ~(1 << event.button); + } + break; + default: + eventType = MouseMove; + if (!buttonsTracked) { + eventButtons = buttons; + } + break; + } + let released = buttons & ~eventButtons; + let pressed = eventButtons & ~buttons; + buttons = eventButtons; + if (eventType === MouseDown && !(pressed & event.button)) { + released |= 1 << event.button; + pressed |= 1 << event.button; + } + if (eventType !== MouseMove && resizing || dragging && (eventType === MouseDown || event.button !== 0)) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } + if (released & 1) { + primaryUp(); + } + if (pressed & 1) { + primaryDown(event); + } + if (eventType === MouseMove) { + onMouseMove(event); + } +} +function primaryDown(event) { + canDrag = false; + canResize = false; + if (!IsWindows()) { + if (event.type === "mousedown" && event.button === 0 && event.detail !== 1) { + return; + } + } + if (resizeEdge) { + canResize = true; + return; + } + const target = eventTarget(event); + const style = window.getComputedStyle(target); + canDrag = style.getPropertyValue("--wails-draggable").trim() === "drag" && (event.offsetX - parseFloat(style.paddingLeft) < target.clientWidth && event.offsetY - parseFloat(style.paddingTop) < target.clientHeight); +} +function primaryUp(event) { + canDrag = false; + dragging = false; + canResize = false; + resizing = false; +} +const cursorForEdge = Object.freeze({ + "se-resize": "nwse-resize", + "sw-resize": "nesw-resize", + "nw-resize": "nwse-resize", + "ne-resize": "nesw-resize", + "w-resize": "ew-resize", + "n-resize": "ns-resize", + "s-resize": "ns-resize", + "e-resize": "ew-resize" +}); +function setResize(edge) { + if (edge) { + if (!resizeEdge) { + defaultCursor = document.body.style.cursor; + } + document.body.style.cursor = cursorForEdge[edge]; + } else if (!edge && resizeEdge) { + document.body.style.cursor = defaultCursor; + } + resizeEdge = edge || ""; +} +function onMouseMove(event) { + if (canResize && resizeEdge) { + resizing = true; + invoke("wails:resize:" + resizeEdge); + } else if (canDrag) { + dragging = true; + invoke("wails:drag"); + } + if (dragging || resizing) { + canDrag = canResize = false; + return; + } + if (!resizable || !IsWindows()) { + if (resizeEdge) { + setResize(); + } + return; + } + const resizeHandleHeight = GetFlag("system.resizeHandleHeight") || 5; + const resizeHandleWidth = GetFlag("system.resizeHandleWidth") || 5; + const cornerExtra = GetFlag("resizeCornerExtra") || 10; + const rightBorder = window.outerWidth - event.clientX < resizeHandleWidth; + const leftBorder = event.clientX < resizeHandleWidth; + const topBorder = event.clientY < resizeHandleHeight; + const bottomBorder = window.outerHeight - event.clientY < resizeHandleHeight; + const rightCorner = window.outerWidth - event.clientX < resizeHandleWidth + cornerExtra; + const leftCorner = event.clientX < resizeHandleWidth + cornerExtra; + const topCorner = event.clientY < resizeHandleHeight + cornerExtra; + const bottomCorner = window.outerHeight - event.clientY < resizeHandleHeight + cornerExtra; + if (!leftCorner && !topCorner && !bottomCorner && !rightCorner) { + setResize(); + } else if (rightCorner && bottomCorner) + setResize("se-resize"); + else if (leftCorner && bottomCorner) + setResize("sw-resize"); + else if (leftCorner && topCorner) + setResize("nw-resize"); + else if (topCorner && rightCorner) + setResize("ne-resize"); + else if (leftBorder) + setResize("w-resize"); + else if (topBorder) + setResize("n-resize"); + else if (bottomBorder) + setResize("s-resize"); + else if (rightBorder) + setResize("e-resize"); + else + setResize(); +} +var fnToStr = Function.prototype.toString; +var reflectApply = typeof Reflect === "object" && Reflect !== null && Reflect.apply; +var badArrayLike; +var isCallableMarker; +if (typeof reflectApply === "function" && typeof Object.defineProperty === "function") { + try { + badArrayLike = Object.defineProperty({}, "length", { + get: function() { + throw isCallableMarker; + } + }); + isCallableMarker = {}; + reflectApply(function() { + throw 42; + }, null, badArrayLike); + } catch (_) { + if (_ !== isCallableMarker) { + reflectApply = null; + } + } +} else { + reflectApply = null; +} +var constructorRegex = /^\s*class\b/; +var isES6ClassFn = function isES6ClassFunction(value) { + try { + var fnStr = fnToStr.call(value); + return constructorRegex.test(fnStr); + } catch (e) { + return false; + } +}; +var tryFunctionObject = function tryFunctionToStr(value) { + try { + if (isES6ClassFn(value)) { + return false; + } + fnToStr.call(value); + return true; + } catch (e) { + return false; + } +}; +var toStr = Object.prototype.toString; +var objectClass = "[object Object]"; +var fnClass = "[object Function]"; +var genClass = "[object GeneratorFunction]"; +var ddaClass = "[object HTMLAllCollection]"; +var ddaClass2 = "[object HTML document.all class]"; +var ddaClass3 = "[object HTMLCollection]"; +var hasToStringTag = typeof Symbol === "function" && !!Symbol.toStringTag; +var isIE68 = !(0 in [,]); +var isDDA = function isDocumentDotAll() { + return false; +}; +if (typeof document === "object") { + var all = document.all; + if (toStr.call(all) === toStr.call(document.all)) { + isDDA = function isDocumentDotAll2(value) { + if ((isIE68 || !value) && (typeof value === "undefined" || typeof value === "object")) { + try { + var str = toStr.call(value); + return (str === ddaClass || str === ddaClass2 || str === ddaClass3 || str === objectClass) && value("") == null; + } catch (e) { + } + } + return false; + }; + } +} +function isCallableRefApply(value) { + if (isDDA(value)) { + return true; + } + if (!value) { + return false; + } + if (typeof value !== "function" && typeof value !== "object") { + return false; + } + try { + reflectApply(value, null, badArrayLike); + } catch (e) { + if (e !== isCallableMarker) { + return false; + } + } + return !isES6ClassFn(value) && tryFunctionObject(value); +} +function isCallableNoRefApply(value) { + if (isDDA(value)) { + return true; + } + if (!value) { + return false; + } + if (typeof value !== "function" && typeof value !== "object") { + return false; + } + if (hasToStringTag) { + return tryFunctionObject(value); + } + if (isES6ClassFn(value)) { + return false; + } + var strClass = toStr.call(value); + if (strClass !== fnClass && strClass !== genClass && !/^\[object HTML/.test(strClass)) { + return false; + } + return tryFunctionObject(value); +} +const isCallable = reflectApply ? isCallableRefApply : isCallableNoRefApply; +var _a; +class CancelError extends Error { + /** + * Constructs a new `CancelError` instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message, options) { + super(message, options); + this.name = "CancelError"; + } +} +class CancelledRejectionError extends Error { + /** + * Constructs a new `CancelledRejectionError` instance. + * @param promise - The promise that caused the error originally. + * @param reason - The rejection reason. + * @param info - An optional informative message specifying the circumstances in which the error was thrown. + * Defaults to the string `"Unhandled rejection in cancelled promise."`. + */ + constructor(promise, reason, info) { + super((info !== null && info !== void 0 ? info : "Unhandled rejection in cancelled promise.") + " Reason: " + errorMessage(reason), { cause: reason }); + this.promise = promise; + this.name = "CancelledRejectionError"; + } +} +const barrierSym = Symbol("barrier"); +const cancelImplSym = Symbol("cancelImpl"); +const species = (_a = Symbol.species) !== null && _a !== void 0 ? _a : Symbol("speciesPolyfill"); +class CancellablePromise extends Promise { + /** + * Creates a new `CancellablePromise`. + * + * @param executor - A callback used to initialize the promise. This callback is passed two arguments: + * a `resolve` callback used to resolve the promise with a value + * or the result of another promise (possibly cancellable), + * and a `reject` callback used to reject the promise with a provided reason or error. + * If the value provided to the `resolve` callback is a thenable _and_ cancellable object + * (it has a `then` _and_ a `cancel` method), + * cancellation requests will be forwarded to that object and the oncancelled will not be invoked anymore. + * If any one of the two callbacks is called _after_ the promise has been cancelled, + * the provided values will be cancelled and resolved as usual, + * but their results will be discarded. + * However, if the resolution process ultimately ends up in a rejection + * that is not due to cancellation, the rejection reason + * will be wrapped in a {@link CancelledRejectionError} + * and bubbled up as an unhandled rejection. + * @param oncancelled - It is the caller's responsibility to ensure that any operation + * started by the executor is properly halted upon cancellation. + * This optional callback can be used to that purpose. + * It will be called _synchronously_ with a cancellation cause + * when cancellation is requested, _after_ the promise has already rejected + * with a {@link CancelError}, but _before_ + * any {@link then}/{@link catch}/{@link finally} callback runs. + * If the callback returns a thenable, the promise returned from {@link cancel} + * will only fulfill after the former has settled. + * Unhandled exceptions or rejections from the callback will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as unhandled rejections. + * If the `resolve` callback is called before cancellation with a cancellable promise, + * cancellation requests on this promise will be diverted to that promise, + * and the original `oncancelled` callback will be discarded. + */ + constructor(executor, oncancelled) { + let resolve; + let reject; + super((res, rej) => { + resolve = res; + reject = rej; + }); + if (this.constructor[species] !== Promise) { + throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property."); + } + let promise = { + promise: this, + resolve, + reject, + get oncancelled() { + return oncancelled !== null && oncancelled !== void 0 ? oncancelled : null; + }, + set oncancelled(cb) { + oncancelled = cb !== null && cb !== void 0 ? cb : void 0; + } + }; + const state = { + get root() { + return state; + }, + resolving: false, + settled: false + }; + void Object.defineProperties(this, { + [barrierSym]: { + configurable: false, + enumerable: false, + writable: true, + value: null + }, + [cancelImplSym]: { + configurable: false, + enumerable: false, + writable: false, + value: cancellerFor(promise, state) + } + }); + const rejector = rejectorFor(promise, state); + try { + executor(resolverFor(promise, state), rejector); + } catch (err) { + if (state.resolving) { + console.log("Unhandled exception in CancellablePromise executor.", err); + } else { + rejector(err); + } + } + } + /** + * Cancels immediately the execution of the operation associated with this promise. + * The promise rejects with a {@link CancelError} instance as reason, + * with the {@link CancelError#cause} property set to the given argument, if any. + * + * Has no effect if called after the promise has already settled; + * repeated calls in particular are safe, but only the first one + * will set the cancellation cause. + * + * The `CancelError` exception _need not_ be handled explicitly _on the promises that are being cancelled:_ + * cancelling a promise with no attached rejection handler does not trigger an unhandled rejection event. + * Therefore, the following idioms are all equally correct: + * ```ts + * new CancellablePromise((resolve, reject) => { ... }).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).catch(...).cancel(); + * ``` + * Whenever some cancelled promise in a chain rejects with a `CancelError` + * with the same cancellation cause as itself, the error will be discarded silently. + * However, the `CancelError` _will still be delivered_ to all attached rejection handlers + * added by {@link then} and related methods: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * cancellable.then(() => { ... }).catch(console.log); + * cancellable.cancel(); // A CancelError is printed to the console. + * ``` + * If the `CancelError` is not handled downstream by the time it reaches + * a _non-cancelled_ promise, it _will_ trigger an unhandled rejection event, + * just like normal rejections would: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * let chained = cancellable.then(() => { ... }).then(() => { ... }); // No catch... + * cancellable.cancel(); // Unhandled rejection event on chained! + * ``` + * Therefore, it is important to either cancel whole promise chains from their tail, + * as shown in the correct idioms above, or take care of handling errors everywhere. + * + * @returns A cancellable promise that _fulfills_ after the cancel callback (if any) + * and all handlers attached up to the call to cancel have run. + * If the cancel callback returns a thenable, the promise returned by `cancel` + * will also wait for that thenable to settle. + * This enables callers to wait for the cancelled operation to terminate + * without being forced to handle potential errors at the call site. + * ```ts + * cancellable.cancel().then(() => { + * // Cleanup finished, it's safe to do something else. + * }, (err) => { + * // Unreachable: the promise returned from cancel will never reject. + * }); + * ``` + * Note that the returned promise will _not_ handle implicitly any rejection + * that might have occurred already in the cancelled chain. + * It will just track whether registered handlers have been executed or not. + * Therefore, unhandled rejections will never be silently handled by calling cancel. + */ + cancel(cause) { + return new CancellablePromise((resolve) => { + Promise.all([ + this[cancelImplSym](new CancelError("Promise cancelled.", { cause })), + currentBarrier(this) + ]).then(() => resolve(), () => resolve()); + }); + } + /** + * Binds promise cancellation to the abort event of the given {@link AbortSignal}. + * If the signal has already aborted, the promise will be cancelled immediately. + * When either condition is verified, the cancellation cause will be set + * to the signal's abort reason (see {@link AbortSignal#reason}). + * + * Has no effect if called (or if the signal aborts) _after_ the promise has already settled. + * Only the first signal to abort will set the cancellation cause. + * + * For more details about the cancellation process, + * see {@link cancel} and the `CancellablePromise` constructor. + * + * This method enables `await`ing cancellable promises without having + * to store them for future cancellation, e.g.: + * ```ts + * await longRunningOperation().cancelOn(signal); + * ``` + * instead of: + * ```ts + * let promiseToBeCancelled = longRunningOperation(); + * await promiseToBeCancelled; + * ``` + * + * @returns This promise, for method chaining. + */ + cancelOn(signal) { + if (signal.aborted) { + void this.cancel(signal.reason); + } else { + signal.addEventListener("abort", () => void this.cancel(signal.reason), { capture: true }); + } + return this; + } + /** + * Attaches callbacks for the resolution and/or rejection of the `CancellablePromise`. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A `CancellablePromise` for the completion of whichever callback is executed. + * The returned promise is hooked up to propagate cancellation requests up the chain, but not down: + * + * - if the parent promise is cancelled, the `onrejected` handler will be invoked with a `CancelError` + * and the returned promise _will resolve regularly_ with its result; + * - conversely, if the returned promise is cancelled, _the parent promise is cancelled too;_ + * the `onrejected` handler will still be invoked with the parent's `CancelError`, + * but its result will be discarded + * and the returned promise will reject with a `CancelError` as well. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If either callback returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + */ + then(onfulfilled, onrejected, oncancelled) { + if (!(this instanceof CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.then called on an invalid object."); + } + if (!isCallable(onfulfilled)) { + onfulfilled = identity; + } + if (!isCallable(onrejected)) { + onrejected = thrower; + } + if (onfulfilled === identity && onrejected == thrower) { + return new CancellablePromise((resolve) => resolve(this)); + } + const barrier = {}; + this[barrierSym] = barrier; + return new CancellablePromise((resolve, reject) => { + void super.then((value) => { + var _a2; + if (this[barrierSym] === barrier) { + this[barrierSym] = null; + } + (_a2 = barrier.resolve) === null || _a2 === void 0 ? void 0 : _a2.call(barrier); + try { + resolve(onfulfilled(value)); + } catch (err) { + reject(err); + } + }, (reason) => { + var _a2; + if (this[barrierSym] === barrier) { + this[barrierSym] = null; + } + (_a2 = barrier.resolve) === null || _a2 === void 0 ? void 0 : _a2.call(barrier); + try { + resolve(onrejected(reason)); + } catch (err) { + reject(err); + } + }); + }, async (cause) => { + try { + return oncancelled === null || oncancelled === void 0 ? void 0 : oncancelled(cause); + } finally { + await this.cancel(cause); + } + }); + } + /** + * Attaches a callback for only the rejection of the Promise. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * It is equivalent to + * ```ts + * cancellablePromise.then(undefined, onrejected, oncancelled); + * ``` + * and the same caveats apply. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onrejected` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + catch(onrejected, oncancelled) { + return this.then(void 0, onrejected, oncancelled); + } + /** + * Attaches a callback that is invoked when the CancellablePromise is settled (fulfilled or rejected). The + * resolved value cannot be accessed or modified from the callback. + * The returned promise will settle in the same state as the original one + * after the provided callback has completed execution, + * unless the callback throws or returns a rejecting promise, + * in which case the returned promise will reject as well. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * Once the parent promise settles, the `onfinally` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * This method is implemented in terms of {@link then} and the same caveats apply. + * It is polyfilled, hence available in every OS/webview version. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onfinally` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + finally(onfinally, oncancelled) { + if (!(this instanceof CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.finally called on an invalid object."); + } + if (!isCallable(onfinally)) { + return this.then(onfinally, onfinally, oncancelled); + } + return this.then((value) => CancellablePromise.resolve(onfinally()).then(() => value), (reason) => CancellablePromise.resolve(onfinally()).then(() => { + throw reason; + }), oncancelled); + } + /** + * We use the `[Symbol.species]` static property, if available, + * to disable the built-in automatic subclassing features from {@link Promise}. + * It is critical for performance reasons that extenders do not override this. + * Once the proposal at https://github.com/tc39/proposal-rm-builtin-subclassing + * is either accepted or retired, this implementation will have to be revised accordingly. + * + * @ignore + * @internal + */ + static get [species]() { + return Promise; + } + static all(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? CancellablePromise.resolve(collected) : new CancellablePromise((resolve, reject) => { + void Promise.all(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static allSettled(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? CancellablePromise.resolve(collected) : new CancellablePromise((resolve, reject) => { + void Promise.allSettled(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static any(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? CancellablePromise.resolve(collected) : new CancellablePromise((resolve, reject) => { + void Promise.any(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static race(values) { + let collected = Array.from(values); + const promise = new CancellablePromise((resolve, reject) => { + void Promise.race(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + /** + * Creates a new cancelled CancellablePromise for the provided cause. + * + * @group Static Methods + */ + static cancel(cause) { + const p = new CancellablePromise(() => { + }); + p.cancel(cause); + return p; + } + /** + * Creates a new CancellablePromise that cancels + * after the specified timeout, with the provided cause. + * + * If the {@link AbortSignal.timeout} factory method is available, + * it is used to base the timeout on _active_ time rather than _elapsed_ time. + * Otherwise, `timeout` falls back to {@link setTimeout}. + * + * @group Static Methods + */ + static timeout(milliseconds, cause) { + const promise = new CancellablePromise(() => { + }); + if (AbortSignal && typeof AbortSignal === "function" && AbortSignal.timeout && typeof AbortSignal.timeout === "function") { + AbortSignal.timeout(milliseconds).addEventListener("abort", () => void promise.cancel(cause)); + } else { + setTimeout(() => void promise.cancel(cause), milliseconds); + } + return promise; + } + static sleep(milliseconds, value) { + return new CancellablePromise((resolve) => { + setTimeout(() => resolve(value), milliseconds); + }); + } + /** + * Creates a new rejected CancellablePromise for the provided reason. + * + * @group Static Methods + */ + static reject(reason) { + return new CancellablePromise((_, reject) => reject(reason)); + } + static resolve(value) { + if (value instanceof CancellablePromise) { + return value; + } + return new CancellablePromise((resolve) => resolve(value)); + } + /** + * Creates a new CancellablePromise and returns it in an object, along with its resolve and reject functions + * and a getter/setter for the cancellation callback. + * + * This method is polyfilled, hence available in every OS/webview version. + * + * @group Static Methods + */ + static withResolvers() { + let result = { oncancelled: null }; + result.promise = new CancellablePromise((resolve, reject) => { + result.resolve = resolve; + result.reject = reject; + }, (cause) => { + var _a2; + (_a2 = result.oncancelled) === null || _a2 === void 0 ? void 0 : _a2.call(result, cause); + }); + return result; + } +} +function cancellerFor(promise, state) { + let cancellationPromise = void 0; + return (reason) => { + if (!state.settled) { + state.settled = true; + state.reason = reason; + promise.reject(reason); + void Promise.prototype.then.call(promise.promise, void 0, (err) => { + if (err !== reason) { + throw err; + } + }); + } + if (!state.reason || !promise.oncancelled) { + return; + } + cancellationPromise = new Promise((resolve) => { + try { + resolve(promise.oncancelled(state.reason.cause)); + } catch (err) { + Promise.reject(new CancelledRejectionError(promise.promise, err, "Unhandled exception in oncancelled callback.")); + } + }).catch((reason2) => { + Promise.reject(new CancelledRejectionError(promise.promise, reason2, "Unhandled rejection in oncancelled callback.")); + }); + promise.oncancelled = null; + return cancellationPromise; + }; +} +function resolverFor(promise, state) { + return (value) => { + if (state.resolving) { + return; + } + state.resolving = true; + if (value === promise.promise) { + if (state.settled) { + return; + } + state.settled = true; + promise.reject(new TypeError("A promise cannot be resolved with itself.")); + return; + } + if (value != null && (typeof value === "object" || typeof value === "function")) { + let then; + try { + then = value.then; + } catch (err) { + state.settled = true; + promise.reject(err); + return; + } + if (isCallable(then)) { + try { + let cancel = value.cancel; + if (isCallable(cancel)) { + const oncancelled = (cause) => { + Reflect.apply(cancel, value, [cause]); + }; + if (state.reason) { + void cancellerFor(Object.assign(Object.assign({}, promise), { oncancelled }), state)(state.reason); + } else { + promise.oncancelled = oncancelled; + } + } + } catch (_a2) { + } + const newState = { + root: state.root, + resolving: false, + get settled() { + return this.root.settled; + }, + set settled(value2) { + this.root.settled = value2; + }, + get reason() { + return this.root.reason; + } + }; + const rejector = rejectorFor(promise, newState); + try { + Reflect.apply(then, value, [resolverFor(promise, newState), rejector]); + } catch (err) { + rejector(err); + } + return; + } + } + if (state.settled) { + return; + } + state.settled = true; + promise.resolve(value); + }; +} +function rejectorFor(promise, state) { + return (reason) => { + if (state.resolving) { + return; + } + state.resolving = true; + if (state.settled) { + try { + if (reason instanceof CancelError && state.reason instanceof CancelError && Object.is(reason.cause, state.reason.cause)) { + return; + } + } catch (_a2) { + } + void Promise.reject(new CancelledRejectionError(promise.promise, reason)); + } else { + state.settled = true; + promise.reject(reason); + } + }; +} +function cancelAll(parent, values, cause) { + const results = []; + for (const value of values) { + let cancel; + try { + if (!isCallable(value.then)) { + continue; + } + cancel = value.cancel; + if (!isCallable(cancel)) { + continue; + } + } catch (_a2) { + continue; + } + let result; + try { + result = Reflect.apply(cancel, value, [cause]); + } catch (err) { + Promise.reject(new CancelledRejectionError(parent, err, "Unhandled exception in cancel method.")); + continue; + } + if (!result) { + continue; + } + results.push((result instanceof Promise ? result : Promise.resolve(result)).catch((reason) => { + Promise.reject(new CancelledRejectionError(parent, reason, "Unhandled rejection in cancel method.")); + })); + } + return Promise.all(results); +} +function identity(x) { + return x; +} +function thrower(reason) { + throw reason; +} +function errorMessage(err) { + try { + if (err instanceof Error || typeof err !== "object" || err.toString !== Object.prototype.toString) { + return "" + err; + } + } catch (_a2) { + } + try { + return JSON.stringify(err); + } catch (_b) { + } + try { + return Object.prototype.toString.call(err); + } catch (_c) { + } + return ""; +} +function currentBarrier(promise) { + var _a2; + let pwr = (_a2 = promise[barrierSym]) !== null && _a2 !== void 0 ? _a2 : {}; + if (!("promise" in pwr)) { + Object.assign(pwr, promiseWithResolvers()); + } + if (promise[barrierSym] == null) { + pwr.resolve(); + promise[barrierSym] = pwr; + } + return pwr.promise; +} +let promiseWithResolvers = Promise.withResolvers; +if (promiseWithResolvers && typeof promiseWithResolvers === "function") { + promiseWithResolvers = promiseWithResolvers.bind(Promise); +} else { + promiseWithResolvers = function() { + let resolve; + let reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; + }; +} +window._wails = window._wails || {}; +window._wails.callResultHandler = resultHandler; +window._wails.callErrorHandler = errorHandler; +const call$1 = newRuntimeCaller(objectNames.Call); +const cancelCall = newRuntimeCaller(objectNames.CancelCall); +const callResponses = /* @__PURE__ */ new Map(); +const CallBinding = 0; +const CancelMethod = 0; +class RuntimeError extends Error { + /** + * Constructs a new RuntimeError instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message, options) { + super(message, options); + this.name = "RuntimeError"; + } +} +function resultHandler(id, data, isJSON) { + const resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + if (!data) { + resolvers.resolve(void 0); + } else if (!isJSON) { + resolvers.resolve(data); + } else { + try { + resolvers.resolve(JSON.parse(data)); + } catch (err) { + resolvers.reject(new TypeError("could not parse result: " + err.message, { cause: err })); + } + } +} +function errorHandler(id, data, isJSON) { + const resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + if (!isJSON) { + resolvers.reject(new Error(data)); + } else { + let error; + try { + error = JSON.parse(data); + } catch (err) { + resolvers.reject(new TypeError("could not parse error: " + err.message, { cause: err })); + return; + } + let options = {}; + if (error.cause) { + options.cause = error.cause; + } + let exception; + switch (error.kind) { + case "ReferenceError": + exception = new ReferenceError(error.message, options); + break; + case "TypeError": + exception = new TypeError(error.message, options); + break; + case "RuntimeError": + exception = new RuntimeError(error.message, options); + break; + default: + exception = new Error(error.message, options); + break; + } + resolvers.reject(exception); + } +} +function getAndDeleteResponse(id) { + const response = callResponses.get(id); + callResponses.delete(id); + return response; +} +function generateID() { + let result; + do { + result = nanoid(); + } while (callResponses.has(result)); + return result; +} +function Call(options) { + const id = generateID(); + const result = CancellablePromise.withResolvers(); + callResponses.set(id, { resolve: result.resolve, reject: result.reject }); + const request = call$1(CallBinding, Object.assign({ "call-id": id }, options)); + let running = false; + request.then(() => { + running = true; + }, (err) => { + callResponses.delete(id); + result.reject(err); + }); + const cancel = () => { + callResponses.delete(id); + return cancelCall(CancelMethod, { "call-id": id }).catch((err) => { + console.error("Error while requesting binding call cancellation:", err); + }); + }; + result.oncancelled = () => { + if (running) { + return cancel(); + } else { + return request.then(cancel); + } + }; + return result.promise; +} +function ByID(methodID, ...args) { + return Call({ methodID, args }); +} +const eventListeners = /* @__PURE__ */ new Map(); +class Listener { + constructor(eventName, callback, maxCallbacks) { + this.eventName = eventName; + this.callback = callback; + this.maxCallbacks = maxCallbacks || -1; + } + dispatch(data) { + try { + this.callback(data); + } catch (err) { + console.error(err); + } + if (this.maxCallbacks === -1) + return false; + this.maxCallbacks -= 1; + return this.maxCallbacks === 0; + } +} +function listenerOff(listener) { + let listeners = eventListeners.get(listener.eventName); + if (!listeners) { + return; + } + listeners = listeners.filter((l) => l !== listener); + if (listeners.length === 0) { + eventListeners.delete(listener.eventName); + } else { + eventListeners.set(listener.eventName, listeners); + } +} +window._wails = window._wails || {}; +window._wails.dispatchWailsEvent = dispatchWailsEvent; +const call = newRuntimeCaller(objectNames.Events); +const EmitMethod = 0; +class WailsEvent { + constructor(name, data = null) { + this.name = name; + this.data = data; + } +} +function dispatchWailsEvent(event) { + let listeners = eventListeners.get(event.name); + if (!listeners) { + return; + } + let wailsEvent = new WailsEvent(event.name, event.data); + if ("sender" in event) { + wailsEvent.sender = event.sender; + } + listeners = listeners.filter((listener) => !listener.dispatch(wailsEvent)); + if (listeners.length === 0) { + eventListeners.delete(event.name); + } else { + eventListeners.set(event.name, listeners); + } +} +function OnMultiple(eventName, callback, maxCallbacks) { + let listeners = eventListeners.get(eventName) || []; + const thisListener = new Listener(eventName, callback, maxCallbacks); + listeners.push(thisListener); + eventListeners.set(eventName, listeners); + return () => listenerOff(thisListener); +} +function On(eventName, callback) { + return OnMultiple(eventName, callback, -1); +} +function Emit(event) { + return call(EmitMethod, event); +} +window._wails = window._wails || {}; +window._wails.invoke = invoke; +invoke("wails:runtime:ready"); +function RemoveBadge() { + return ByID(2752757297); +} +function SetBadge(label) { + return ByID(1717705661, label); +} +function SetCustomBadge(label, options) { + return ByID(2730169760, label, options); +} +class RGBA { + /** Creates a new RGBA instance. */ + constructor($$source = {}) { + __publicField(this, "R"); + __publicField(this, "G"); + __publicField(this, "B"); + __publicField(this, "A"); + if (!("R" in $$source)) { + this["R"] = 0; + } + if (!("G" in $$source)) { + this["G"] = 0; + } + if (!("B" in $$source)) { + this["B"] = 0; + } + if (!("A" in $$source)) { + this["A"] = 0; + } + Object.assign(this, $$source); + } + /** + * Creates a new RGBA instance from a string or object. + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === "string" ? JSON.parse($$source) : $$source; + return new RGBA($$parsedSource); + } +} +const setCustomButton = document.getElementById("set-custom"); +const setButton = document.getElementById("set"); +const removeButton = document.getElementById("remove"); +const setButtonUsingGo = document.getElementById("set-go"); +const removeButtonUsingGo = document.getElementById("remove-go"); +const labelElement = document.getElementById("label"); +const timeElement = document.getElementById("time"); +setCustomButton.addEventListener("click", () => { + console.log("click!"); + let label = labelElement.value; + SetCustomBadge(label, { + BackgroundColour: RGBA.createFrom({ + R: 0, + G: 255, + B: 255, + A: 255 + }), + FontName: "arialb.ttf", + // System font + FontSize: 16, + SmallFontSize: 10, + TextColour: RGBA.createFrom({ + R: 0, + G: 0, + B: 0, + A: 255 + }) + }); +}); +setButton.addEventListener("click", () => { + let label = labelElement.value; + SetBadge(label); +}); +removeButton.addEventListener("click", () => { + RemoveBadge(); +}); +setButtonUsingGo.addEventListener("click", () => { + let label = labelElement.value; + void Emit({ + name: "set:badge", + data: label + }); +}); +removeButtonUsingGo.addEventListener("click", () => { + void Emit({ name: "remove:badge", data: null }); +}); +On("time", (time) => { + timeElement.innerText = time.data; +}); diff --git a/v3/examples/badge-custom/frontend/dist/index.html b/v3/examples/badge-custom/frontend/dist/index.html index d785892ac..11b9fcd50 100644 --- a/v3/examples/badge-custom/frontend/dist/index.html +++ b/v3/examples/badge-custom/frontend/dist/index.html @@ -6,7 +6,7 @@ Wails App - +
diff --git a/v3/examples/badge-custom/frontend/src/main.ts b/v3/examples/badge-custom/frontend/src/main.ts index 405f4fe28..862185f3f 100644 --- a/v3/examples/badge-custom/frontend/src/main.ts +++ b/v3/examples/badge-custom/frontend/src/main.ts @@ -1,5 +1,5 @@ import {Events} from "@wailsio/runtime"; -import {SetBadge, RemoveBadge, SetCustomBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice"; +import {SetBadge, RemoveBadge, SetCustomBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice"; import { RGBA } from "../bindings/image/color/models"; const setCustomButton = document.getElementById('set-custom')! as HTMLButtonElement; diff --git a/v3/examples/badge-custom/main.go b/v3/examples/badge-custom/main.go index e27103443..93a32d1ff 100644 --- a/v3/examples/badge-custom/main.go +++ b/v3/examples/badge-custom/main.go @@ -8,7 +8,7 @@ import ( "time" "github.com/wailsapp/wails/v3/pkg/application" - "github.com/wailsapp/wails/v3/pkg/services/badge" + "github.com/wailsapp/wails/v3/pkg/services/dock" ) // Wails uses Go's `embed` package to embed the frontend files into the binary. @@ -29,7 +29,7 @@ func main() { // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. // 'Mac' options tailor the application when running an macOS. - badgeService := badge.NewWithOptions(badge.Options{ + dockService := dock.NewWithOptions(dock.BadgeOptions{ TextColour: color.RGBA{255, 255, 204, 255}, BackgroundColour: color.RGBA{16, 124, 16, 255}, FontName: "consolab.ttf", @@ -41,7 +41,7 @@ func main() { Name: "badge", Description: "A demo of using raw HTML & CSS", Services: []application.Service{ - application.NewService(badgeService), + application.NewService(dockService), }, Assets: application.AssetOptions{ Handler: application.AssetFileServerFS(assets), @@ -68,7 +68,7 @@ func main() { }) app.Event.On("remove:badge", func(event *application.CustomEvent) { - err := badgeService.RemoveBadge() + err := dockService.RemoveBadge() if err != nil { log.Fatal(err) } @@ -76,7 +76,7 @@ func main() { app.Event.On("set:badge", func(event *application.CustomEvent) { text := event.Data.(string) - err := badgeService.SetBadge(text) + err := dockService.SetBadge(text) if err != nil { log.Fatal(err) } diff --git a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts deleted file mode 100644 index f959d50dc..000000000 --- a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -/** - * Service represents the notifications service - * @module - */ - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import * as $models from "./models.js"; - -/** - * RemoveBadge removes the badge label from the application icon. - */ -export function RemoveBadge(): $CancellablePromise { - return $Call.ByID(2374916939); -} - -/** - * SetBadge sets the badge label on the application icon. - */ -export function SetBadge(label: string): $CancellablePromise { - return $Call.ByID(784276339, label); -} - -export function SetCustomBadge(label: string, options: $models.Options): $CancellablePromise { - return $Call.ByID(3058653106, label, options); -} diff --git a/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts new file mode 100644 index 000000000..abf1f22e4 --- /dev/null +++ b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts @@ -0,0 +1,53 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service represents the dock service + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * HideAppIcon hides the app icon in the dock/taskbar. + */ +export function HideAppIcon(): $CancellablePromise { + return $Call.ByID(3413658144); +} + +/** + * RemoveBadge removes the badge label from the application icon. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function RemoveBadge(): $CancellablePromise { + return $Call.ByID(2752757297); +} + +/** + * SetBadge sets the badge label on the application icon. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function SetBadge(label: string): $CancellablePromise { + return $Call.ByID(1717705661, label); +} + +/** + * SetCustomBadge sets the badge label on the application icon with custom options. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function SetCustomBadge(label: string, options: $models.BadgeOptions): $CancellablePromise { + return $Call.ByID(2730169760, label, options); +} + +/** + * ShowAppIcon shows the app icon in the dock/taskbar. + */ +export function ShowAppIcon(): $CancellablePromise { + return $Call.ByID(3409697379); +} diff --git a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts similarity index 65% rename from v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts rename to v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts index df3ea9723..fbdaf19f3 100644 --- a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/index.ts +++ b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts @@ -1,11 +1,11 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -import * as BadgeService from "./badgeservice.js"; +import * as DockService from "./dockservice.js"; export { - BadgeService + DockService }; export { - Options + BadgeOptions } from "./models.js"; diff --git a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts similarity index 80% rename from v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts rename to v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts index 67ed264c0..f97c8a4c6 100644 --- a/v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/badge/models.ts +++ b/v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts @@ -9,15 +9,18 @@ import { Create as $Create } from "@wailsio/runtime"; // @ts-ignore: Unused imports import * as color$0 from "../../../../../../../image/color/models.js"; -export class Options { +/** + * BadgeOptions represents options for customizing badge appearance + */ +export class BadgeOptions { "TextColour": color$0.RGBA; "BackgroundColour": color$0.RGBA; "FontName": string; "FontSize": number; "SmallFontSize": number; - /** Creates a new Options instance. */ - constructor($$source: Partial = {}) { + /** Creates a new BadgeOptions instance. */ + constructor($$source: Partial = {}) { if (!("TextColour" in $$source)) { this["TextColour"] = (new color$0.RGBA()); } @@ -38,9 +41,9 @@ export class Options { } /** - * Creates a new Options instance from a string or object. + * Creates a new BadgeOptions instance from a string or object. */ - static createFrom($$source: any = {}): Options { + static createFrom($$source: any = {}): BadgeOptions { const $$createField0_0 = $$createType0; const $$createField1_0 = $$createType0; let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; @@ -50,7 +53,7 @@ export class Options { if ("BackgroundColour" in $$parsedSource) { $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]); } - return new Options($$parsedSource as Partial); + return new BadgeOptions($$parsedSource as Partial); } } diff --git a/v3/examples/badge/frontend/dist/assets/index-CtfGGYXM.js b/v3/examples/badge/frontend/dist/assets/index-CtfGGYXM.js new file mode 100644 index 000000000..6149df5db --- /dev/null +++ b/v3/examples/badge/frontend/dist/assets/index-CtfGGYXM.js @@ -0,0 +1,1357 @@ +(function polyfill() { + const relList = document.createElement("link").relList; + if (relList && relList.supports && relList.supports("modulepreload")) { + return; + } + for (const link of document.querySelectorAll('link[rel="modulepreload"]')) { + processPreload(link); + } + new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.type !== "childList") { + continue; + } + for (const node of mutation.addedNodes) { + if (node.tagName === "LINK" && node.rel === "modulepreload") + processPreload(node); + } + } + }).observe(document, { childList: true, subtree: true }); + function getFetchOpts(link) { + const fetchOpts = {}; + if (link.integrity) fetchOpts.integrity = link.integrity; + if (link.referrerPolicy) fetchOpts.referrerPolicy = link.referrerPolicy; + if (link.crossOrigin === "use-credentials") + fetchOpts.credentials = "include"; + else if (link.crossOrigin === "anonymous") fetchOpts.credentials = "omit"; + else fetchOpts.credentials = "same-origin"; + return fetchOpts; + } + function processPreload(link) { + if (link.ep) + return; + link.ep = true; + const fetchOpts = getFetchOpts(link); + fetch(link.href, fetchOpts); + } +})(); +const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"; +function nanoid(size = 21) { + let id = ""; + let i = size | 0; + while (i--) { + id += urlAlphabet[Math.random() * 64 | 0]; + } + return id; +} +const runtimeURL = window.location.origin + "/wails/runtime"; +const objectNames = Object.freeze({ + Call: 0, + Clipboard: 1, + Application: 2, + Events: 3, + ContextMenu: 4, + Dialog: 5, + Window: 6, + Screens: 7, + System: 8, + Browser: 9, + CancelCall: 10 +}); +let clientId = nanoid(); +function newRuntimeCaller(object, windowName = "") { + return function(method, args = null) { + return runtimeCallWithID(object, method, windowName, args); + }; +} +async function runtimeCallWithID(objectID, method, windowName, args) { + var _a2, _b; + let url = new URL(runtimeURL); + url.searchParams.append("object", objectID.toString()); + url.searchParams.append("method", method.toString()); + if (args) { + url.searchParams.append("args", JSON.stringify(args)); + } + let headers = { + ["x-wails-client-id"]: clientId + }; + if (windowName) { + headers["x-wails-window-name"] = windowName; + } + let response = await fetch(url, { headers }); + if (!response.ok) { + throw new Error(await response.text()); + } + if (((_b = (_a2 = response.headers.get("Content-Type")) === null || _a2 === void 0 ? void 0 : _a2.indexOf("application/json")) !== null && _b !== void 0 ? _b : -1) !== -1) { + return response.json(); + } else { + return response.text(); + } +} +newRuntimeCaller(objectNames.System); +const _invoke = function() { + var _a2, _b, _c, _d, _e; + try { + if ((_b = (_a2 = window.chrome) === null || _a2 === void 0 ? void 0 : _a2.webview) === null || _b === void 0 ? void 0 : _b.postMessage) { + return window.chrome.webview.postMessage.bind(window.chrome.webview); + } else if ((_e = (_d = (_c = window.webkit) === null || _c === void 0 ? void 0 : _c.messageHandlers) === null || _d === void 0 ? void 0 : _d["external"]) === null || _e === void 0 ? void 0 : _e.postMessage) { + return window.webkit.messageHandlers["external"].postMessage.bind(window.webkit.messageHandlers["external"]); + } + } catch (e) { + } + console.warn("\n%c⚠️ Browser Environment Detected %c\n\n%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode.\nMore information at: https://v3.wails.io/learn/build/#using-a-browser-for-development\n", "background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;", "background: transparent;", "color: #ffffff; font-style: italic; font-weight: bold;"); + return null; +}(); +function invoke(msg) { + _invoke === null || _invoke === void 0 ? void 0 : _invoke(msg); +} +function IsWindows() { + return window._wails.environment.OS === "windows"; +} +function IsDebug() { + return Boolean(window._wails.environment.Debug); +} +function canTrackButtons() { + return new MouseEvent("mousedown").buttons === 0; +} +function eventTarget(event) { + var _a2; + if (event.target instanceof HTMLElement) { + return event.target; + } else if (!(event.target instanceof HTMLElement) && event.target instanceof Node) { + return (_a2 = event.target.parentElement) !== null && _a2 !== void 0 ? _a2 : document.body; + } else { + return document.body; + } +} +document.addEventListener("DOMContentLoaded", () => { +}); +window.addEventListener("contextmenu", contextMenuHandler); +const call$2 = newRuntimeCaller(objectNames.ContextMenu); +const ContextMenuOpen = 0; +function openContextMenu(id, x, y, data) { + void call$2(ContextMenuOpen, { id, x, y, data }); +} +function contextMenuHandler(event) { + const target = eventTarget(event); + const customContextMenu = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu").trim(); + if (customContextMenu) { + event.preventDefault(); + const data = window.getComputedStyle(target).getPropertyValue("--custom-contextmenu-data"); + openContextMenu(customContextMenu, event.clientX, event.clientY, data); + } else { + processDefaultContextMenu(event, target); + } +} +function processDefaultContextMenu(event, target) { + if (IsDebug()) { + return; + } + switch (window.getComputedStyle(target).getPropertyValue("--default-contextmenu").trim()) { + case "show": + return; + case "hide": + event.preventDefault(); + return; + } + if (target.isContentEditable) { + return; + } + const selection = window.getSelection(); + const hasSelection = selection && selection.toString().length > 0; + if (hasSelection) { + for (let i = 0; i < selection.rangeCount; i++) { + const range = selection.getRangeAt(i); + const rects = range.getClientRects(); + for (let j = 0; j < rects.length; j++) { + const rect = rects[j]; + if (document.elementFromPoint(rect.left, rect.top) === target) { + return; + } + } + } + } + if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { + if (hasSelection || !target.readOnly && !target.disabled) { + return; + } + } + event.preventDefault(); +} +function GetFlag(key) { + try { + return window._wails.flags[key]; + } catch (e) { + throw new Error("Unable to retrieve flag '" + key + "': " + e, { cause: e }); + } +} +let canDrag = false; +let dragging = false; +let resizable = false; +let canResize = false; +let resizing = false; +let resizeEdge = ""; +let defaultCursor = "auto"; +let buttons = 0; +const buttonsTracked = canTrackButtons(); +window._wails = window._wails || {}; +window._wails.setResizable = (value) => { + resizable = value; + if (!resizable) { + canResize = resizing = false; + setResize(); + } +}; +window.addEventListener("mousedown", update, { capture: true }); +window.addEventListener("mousemove", update, { capture: true }); +window.addEventListener("mouseup", update, { capture: true }); +for (const ev of ["click", "contextmenu", "dblclick"]) { + window.addEventListener(ev, suppressEvent, { capture: true }); +} +function suppressEvent(event) { + if (dragging || resizing) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } +} +const MouseDown = 0; +const MouseUp = 1; +const MouseMove = 2; +function update(event) { + let eventType, eventButtons = event.buttons; + switch (event.type) { + case "mousedown": + eventType = MouseDown; + if (!buttonsTracked) { + eventButtons = buttons | 1 << event.button; + } + break; + case "mouseup": + eventType = MouseUp; + if (!buttonsTracked) { + eventButtons = buttons & ~(1 << event.button); + } + break; + default: + eventType = MouseMove; + if (!buttonsTracked) { + eventButtons = buttons; + } + break; + } + let released = buttons & ~eventButtons; + let pressed = eventButtons & ~buttons; + buttons = eventButtons; + if (eventType === MouseDown && !(pressed & event.button)) { + released |= 1 << event.button; + pressed |= 1 << event.button; + } + if (eventType !== MouseMove && resizing || dragging && (eventType === MouseDown || event.button !== 0)) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } + if (released & 1) { + primaryUp(); + } + if (pressed & 1) { + primaryDown(event); + } + if (eventType === MouseMove) { + onMouseMove(event); + } +} +function primaryDown(event) { + canDrag = false; + canResize = false; + if (!IsWindows()) { + if (event.type === "mousedown" && event.button === 0 && event.detail !== 1) { + return; + } + } + if (resizeEdge) { + canResize = true; + return; + } + const target = eventTarget(event); + const style = window.getComputedStyle(target); + canDrag = style.getPropertyValue("--wails-draggable").trim() === "drag" && (event.offsetX - parseFloat(style.paddingLeft) < target.clientWidth && event.offsetY - parseFloat(style.paddingTop) < target.clientHeight); +} +function primaryUp(event) { + canDrag = false; + dragging = false; + canResize = false; + resizing = false; +} +const cursorForEdge = Object.freeze({ + "se-resize": "nwse-resize", + "sw-resize": "nesw-resize", + "nw-resize": "nwse-resize", + "ne-resize": "nesw-resize", + "w-resize": "ew-resize", + "n-resize": "ns-resize", + "s-resize": "ns-resize", + "e-resize": "ew-resize" +}); +function setResize(edge) { + if (edge) { + if (!resizeEdge) { + defaultCursor = document.body.style.cursor; + } + document.body.style.cursor = cursorForEdge[edge]; + } else if (!edge && resizeEdge) { + document.body.style.cursor = defaultCursor; + } + resizeEdge = edge || ""; +} +function onMouseMove(event) { + if (canResize && resizeEdge) { + resizing = true; + invoke("wails:resize:" + resizeEdge); + } else if (canDrag) { + dragging = true; + invoke("wails:drag"); + } + if (dragging || resizing) { + canDrag = canResize = false; + return; + } + if (!resizable || !IsWindows()) { + if (resizeEdge) { + setResize(); + } + return; + } + const resizeHandleHeight = GetFlag("system.resizeHandleHeight") || 5; + const resizeHandleWidth = GetFlag("system.resizeHandleWidth") || 5; + const cornerExtra = GetFlag("resizeCornerExtra") || 10; + const rightBorder = window.outerWidth - event.clientX < resizeHandleWidth; + const leftBorder = event.clientX < resizeHandleWidth; + const topBorder = event.clientY < resizeHandleHeight; + const bottomBorder = window.outerHeight - event.clientY < resizeHandleHeight; + const rightCorner = window.outerWidth - event.clientX < resizeHandleWidth + cornerExtra; + const leftCorner = event.clientX < resizeHandleWidth + cornerExtra; + const topCorner = event.clientY < resizeHandleHeight + cornerExtra; + const bottomCorner = window.outerHeight - event.clientY < resizeHandleHeight + cornerExtra; + if (!leftCorner && !topCorner && !bottomCorner && !rightCorner) { + setResize(); + } else if (rightCorner && bottomCorner) + setResize("se-resize"); + else if (leftCorner && bottomCorner) + setResize("sw-resize"); + else if (leftCorner && topCorner) + setResize("nw-resize"); + else if (topCorner && rightCorner) + setResize("ne-resize"); + else if (leftBorder) + setResize("w-resize"); + else if (topBorder) + setResize("n-resize"); + else if (bottomBorder) + setResize("s-resize"); + else if (rightBorder) + setResize("e-resize"); + else + setResize(); +} +var fnToStr = Function.prototype.toString; +var reflectApply = typeof Reflect === "object" && Reflect !== null && Reflect.apply; +var badArrayLike; +var isCallableMarker; +if (typeof reflectApply === "function" && typeof Object.defineProperty === "function") { + try { + badArrayLike = Object.defineProperty({}, "length", { + get: function() { + throw isCallableMarker; + } + }); + isCallableMarker = {}; + reflectApply(function() { + throw 42; + }, null, badArrayLike); + } catch (_) { + if (_ !== isCallableMarker) { + reflectApply = null; + } + } +} else { + reflectApply = null; +} +var constructorRegex = /^\s*class\b/; +var isES6ClassFn = function isES6ClassFunction(value) { + try { + var fnStr = fnToStr.call(value); + return constructorRegex.test(fnStr); + } catch (e) { + return false; + } +}; +var tryFunctionObject = function tryFunctionToStr(value) { + try { + if (isES6ClassFn(value)) { + return false; + } + fnToStr.call(value); + return true; + } catch (e) { + return false; + } +}; +var toStr = Object.prototype.toString; +var objectClass = "[object Object]"; +var fnClass = "[object Function]"; +var genClass = "[object GeneratorFunction]"; +var ddaClass = "[object HTMLAllCollection]"; +var ddaClass2 = "[object HTML document.all class]"; +var ddaClass3 = "[object HTMLCollection]"; +var hasToStringTag = typeof Symbol === "function" && !!Symbol.toStringTag; +var isIE68 = !(0 in [,]); +var isDDA = function isDocumentDotAll() { + return false; +}; +if (typeof document === "object") { + var all = document.all; + if (toStr.call(all) === toStr.call(document.all)) { + isDDA = function isDocumentDotAll2(value) { + if ((isIE68 || !value) && (typeof value === "undefined" || typeof value === "object")) { + try { + var str = toStr.call(value); + return (str === ddaClass || str === ddaClass2 || str === ddaClass3 || str === objectClass) && value("") == null; + } catch (e) { + } + } + return false; + }; + } +} +function isCallableRefApply(value) { + if (isDDA(value)) { + return true; + } + if (!value) { + return false; + } + if (typeof value !== "function" && typeof value !== "object") { + return false; + } + try { + reflectApply(value, null, badArrayLike); + } catch (e) { + if (e !== isCallableMarker) { + return false; + } + } + return !isES6ClassFn(value) && tryFunctionObject(value); +} +function isCallableNoRefApply(value) { + if (isDDA(value)) { + return true; + } + if (!value) { + return false; + } + if (typeof value !== "function" && typeof value !== "object") { + return false; + } + if (hasToStringTag) { + return tryFunctionObject(value); + } + if (isES6ClassFn(value)) { + return false; + } + var strClass = toStr.call(value); + if (strClass !== fnClass && strClass !== genClass && !/^\[object HTML/.test(strClass)) { + return false; + } + return tryFunctionObject(value); +} +const isCallable = reflectApply ? isCallableRefApply : isCallableNoRefApply; +var _a; +class CancelError extends Error { + /** + * Constructs a new `CancelError` instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message, options) { + super(message, options); + this.name = "CancelError"; + } +} +class CancelledRejectionError extends Error { + /** + * Constructs a new `CancelledRejectionError` instance. + * @param promise - The promise that caused the error originally. + * @param reason - The rejection reason. + * @param info - An optional informative message specifying the circumstances in which the error was thrown. + * Defaults to the string `"Unhandled rejection in cancelled promise."`. + */ + constructor(promise, reason, info) { + super((info !== null && info !== void 0 ? info : "Unhandled rejection in cancelled promise.") + " Reason: " + errorMessage(reason), { cause: reason }); + this.promise = promise; + this.name = "CancelledRejectionError"; + } +} +const barrierSym = Symbol("barrier"); +const cancelImplSym = Symbol("cancelImpl"); +const species = (_a = Symbol.species) !== null && _a !== void 0 ? _a : Symbol("speciesPolyfill"); +class CancellablePromise extends Promise { + /** + * Creates a new `CancellablePromise`. + * + * @param executor - A callback used to initialize the promise. This callback is passed two arguments: + * a `resolve` callback used to resolve the promise with a value + * or the result of another promise (possibly cancellable), + * and a `reject` callback used to reject the promise with a provided reason or error. + * If the value provided to the `resolve` callback is a thenable _and_ cancellable object + * (it has a `then` _and_ a `cancel` method), + * cancellation requests will be forwarded to that object and the oncancelled will not be invoked anymore. + * If any one of the two callbacks is called _after_ the promise has been cancelled, + * the provided values will be cancelled and resolved as usual, + * but their results will be discarded. + * However, if the resolution process ultimately ends up in a rejection + * that is not due to cancellation, the rejection reason + * will be wrapped in a {@link CancelledRejectionError} + * and bubbled up as an unhandled rejection. + * @param oncancelled - It is the caller's responsibility to ensure that any operation + * started by the executor is properly halted upon cancellation. + * This optional callback can be used to that purpose. + * It will be called _synchronously_ with a cancellation cause + * when cancellation is requested, _after_ the promise has already rejected + * with a {@link CancelError}, but _before_ + * any {@link then}/{@link catch}/{@link finally} callback runs. + * If the callback returns a thenable, the promise returned from {@link cancel} + * will only fulfill after the former has settled. + * Unhandled exceptions or rejections from the callback will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as unhandled rejections. + * If the `resolve` callback is called before cancellation with a cancellable promise, + * cancellation requests on this promise will be diverted to that promise, + * and the original `oncancelled` callback will be discarded. + */ + constructor(executor, oncancelled) { + let resolve; + let reject; + super((res, rej) => { + resolve = res; + reject = rej; + }); + if (this.constructor[species] !== Promise) { + throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property."); + } + let promise = { + promise: this, + resolve, + reject, + get oncancelled() { + return oncancelled !== null && oncancelled !== void 0 ? oncancelled : null; + }, + set oncancelled(cb) { + oncancelled = cb !== null && cb !== void 0 ? cb : void 0; + } + }; + const state = { + get root() { + return state; + }, + resolving: false, + settled: false + }; + void Object.defineProperties(this, { + [barrierSym]: { + configurable: false, + enumerable: false, + writable: true, + value: null + }, + [cancelImplSym]: { + configurable: false, + enumerable: false, + writable: false, + value: cancellerFor(promise, state) + } + }); + const rejector = rejectorFor(promise, state); + try { + executor(resolverFor(promise, state), rejector); + } catch (err) { + if (state.resolving) { + console.log("Unhandled exception in CancellablePromise executor.", err); + } else { + rejector(err); + } + } + } + /** + * Cancels immediately the execution of the operation associated with this promise. + * The promise rejects with a {@link CancelError} instance as reason, + * with the {@link CancelError#cause} property set to the given argument, if any. + * + * Has no effect if called after the promise has already settled; + * repeated calls in particular are safe, but only the first one + * will set the cancellation cause. + * + * The `CancelError` exception _need not_ be handled explicitly _on the promises that are being cancelled:_ + * cancelling a promise with no attached rejection handler does not trigger an unhandled rejection event. + * Therefore, the following idioms are all equally correct: + * ```ts + * new CancellablePromise((resolve, reject) => { ... }).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).cancel(); + * new CancellablePromise((resolve, reject) => { ... }).then(...).catch(...).cancel(); + * ``` + * Whenever some cancelled promise in a chain rejects with a `CancelError` + * with the same cancellation cause as itself, the error will be discarded silently. + * However, the `CancelError` _will still be delivered_ to all attached rejection handlers + * added by {@link then} and related methods: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * cancellable.then(() => { ... }).catch(console.log); + * cancellable.cancel(); // A CancelError is printed to the console. + * ``` + * If the `CancelError` is not handled downstream by the time it reaches + * a _non-cancelled_ promise, it _will_ trigger an unhandled rejection event, + * just like normal rejections would: + * ```ts + * let cancellable = new CancellablePromise((resolve, reject) => { ... }); + * let chained = cancellable.then(() => { ... }).then(() => { ... }); // No catch... + * cancellable.cancel(); // Unhandled rejection event on chained! + * ``` + * Therefore, it is important to either cancel whole promise chains from their tail, + * as shown in the correct idioms above, or take care of handling errors everywhere. + * + * @returns A cancellable promise that _fulfills_ after the cancel callback (if any) + * and all handlers attached up to the call to cancel have run. + * If the cancel callback returns a thenable, the promise returned by `cancel` + * will also wait for that thenable to settle. + * This enables callers to wait for the cancelled operation to terminate + * without being forced to handle potential errors at the call site. + * ```ts + * cancellable.cancel().then(() => { + * // Cleanup finished, it's safe to do something else. + * }, (err) => { + * // Unreachable: the promise returned from cancel will never reject. + * }); + * ``` + * Note that the returned promise will _not_ handle implicitly any rejection + * that might have occurred already in the cancelled chain. + * It will just track whether registered handlers have been executed or not. + * Therefore, unhandled rejections will never be silently handled by calling cancel. + */ + cancel(cause) { + return new CancellablePromise((resolve) => { + Promise.all([ + this[cancelImplSym](new CancelError("Promise cancelled.", { cause })), + currentBarrier(this) + ]).then(() => resolve(), () => resolve()); + }); + } + /** + * Binds promise cancellation to the abort event of the given {@link AbortSignal}. + * If the signal has already aborted, the promise will be cancelled immediately. + * When either condition is verified, the cancellation cause will be set + * to the signal's abort reason (see {@link AbortSignal#reason}). + * + * Has no effect if called (or if the signal aborts) _after_ the promise has already settled. + * Only the first signal to abort will set the cancellation cause. + * + * For more details about the cancellation process, + * see {@link cancel} and the `CancellablePromise` constructor. + * + * This method enables `await`ing cancellable promises without having + * to store them for future cancellation, e.g.: + * ```ts + * await longRunningOperation().cancelOn(signal); + * ``` + * instead of: + * ```ts + * let promiseToBeCancelled = longRunningOperation(); + * await promiseToBeCancelled; + * ``` + * + * @returns This promise, for method chaining. + */ + cancelOn(signal) { + if (signal.aborted) { + void this.cancel(signal.reason); + } else { + signal.addEventListener("abort", () => void this.cancel(signal.reason), { capture: true }); + } + return this; + } + /** + * Attaches callbacks for the resolution and/or rejection of the `CancellablePromise`. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A `CancellablePromise` for the completion of whichever callback is executed. + * The returned promise is hooked up to propagate cancellation requests up the chain, but not down: + * + * - if the parent promise is cancelled, the `onrejected` handler will be invoked with a `CancelError` + * and the returned promise _will resolve regularly_ with its result; + * - conversely, if the returned promise is cancelled, _the parent promise is cancelled too;_ + * the `onrejected` handler will still be invoked with the parent's `CancelError`, + * but its result will be discarded + * and the returned promise will reject with a `CancelError` as well. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If either callback returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + */ + then(onfulfilled, onrejected, oncancelled) { + if (!(this instanceof CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.then called on an invalid object."); + } + if (!isCallable(onfulfilled)) { + onfulfilled = identity; + } + if (!isCallable(onrejected)) { + onrejected = thrower; + } + if (onfulfilled === identity && onrejected == thrower) { + return new CancellablePromise((resolve) => resolve(this)); + } + const barrier = {}; + this[barrierSym] = barrier; + return new CancellablePromise((resolve, reject) => { + void super.then((value) => { + var _a2; + if (this[barrierSym] === barrier) { + this[barrierSym] = null; + } + (_a2 = barrier.resolve) === null || _a2 === void 0 ? void 0 : _a2.call(barrier); + try { + resolve(onfulfilled(value)); + } catch (err) { + reject(err); + } + }, (reason) => { + var _a2; + if (this[barrierSym] === barrier) { + this[barrierSym] = null; + } + (_a2 = barrier.resolve) === null || _a2 === void 0 ? void 0 : _a2.call(barrier); + try { + resolve(onrejected(reason)); + } catch (err) { + reject(err); + } + }); + }, async (cause) => { + try { + return oncancelled === null || oncancelled === void 0 ? void 0 : oncancelled(cause); + } finally { + await this.cancel(cause); + } + }); + } + /** + * Attaches a callback for only the rejection of the Promise. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * When the parent promise rejects or is cancelled, the `onrejected` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * It is equivalent to + * ```ts + * cancellablePromise.then(undefined, onrejected, oncancelled); + * ``` + * and the same caveats apply. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onrejected` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + catch(onrejected, oncancelled) { + return this.then(void 0, onrejected, oncancelled); + } + /** + * Attaches a callback that is invoked when the CancellablePromise is settled (fulfilled or rejected). The + * resolved value cannot be accessed or modified from the callback. + * The returned promise will settle in the same state as the original one + * after the provided callback has completed execution, + * unless the callback throws or returns a rejecting promise, + * in which case the returned promise will reject as well. + * + * The optional `oncancelled` argument will be invoked when the returned promise is cancelled, + * with the same semantics as the `oncancelled` argument of the constructor. + * Once the parent promise settles, the `onfinally` callback will run, + * _even after the returned promise has been cancelled:_ + * in that case, should it reject or throw, the reason will be wrapped + * in a {@link CancelledRejectionError} and bubbled up as an unhandled rejection. + * + * This method is implemented in terms of {@link then} and the same caveats apply. + * It is polyfilled, hence available in every OS/webview version. + * + * @returns A Promise for the completion of the callback. + * Cancellation requests on the returned promise + * will propagate up the chain to the parent promise, + * but not in the other direction. + * + * The promise returned from {@link cancel} will fulfill only after all attached handlers + * up the entire promise chain have been run. + * + * If `onfinally` returns a cancellable promise, + * cancellation requests will be diverted to it, + * and the specified `oncancelled` callback will be discarded. + * See {@link then} for more details. + */ + finally(onfinally, oncancelled) { + if (!(this instanceof CancellablePromise)) { + throw new TypeError("CancellablePromise.prototype.finally called on an invalid object."); + } + if (!isCallable(onfinally)) { + return this.then(onfinally, onfinally, oncancelled); + } + return this.then((value) => CancellablePromise.resolve(onfinally()).then(() => value), (reason) => CancellablePromise.resolve(onfinally()).then(() => { + throw reason; + }), oncancelled); + } + /** + * We use the `[Symbol.species]` static property, if available, + * to disable the built-in automatic subclassing features from {@link Promise}. + * It is critical for performance reasons that extenders do not override this. + * Once the proposal at https://github.com/tc39/proposal-rm-builtin-subclassing + * is either accepted or retired, this implementation will have to be revised accordingly. + * + * @ignore + * @internal + */ + static get [species]() { + return Promise; + } + static all(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? CancellablePromise.resolve(collected) : new CancellablePromise((resolve, reject) => { + void Promise.all(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static allSettled(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? CancellablePromise.resolve(collected) : new CancellablePromise((resolve, reject) => { + void Promise.allSettled(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static any(values) { + let collected = Array.from(values); + const promise = collected.length === 0 ? CancellablePromise.resolve(collected) : new CancellablePromise((resolve, reject) => { + void Promise.any(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + static race(values) { + let collected = Array.from(values); + const promise = new CancellablePromise((resolve, reject) => { + void Promise.race(collected).then(resolve, reject); + }, (cause) => cancelAll(promise, collected, cause)); + return promise; + } + /** + * Creates a new cancelled CancellablePromise for the provided cause. + * + * @group Static Methods + */ + static cancel(cause) { + const p = new CancellablePromise(() => { + }); + p.cancel(cause); + return p; + } + /** + * Creates a new CancellablePromise that cancels + * after the specified timeout, with the provided cause. + * + * If the {@link AbortSignal.timeout} factory method is available, + * it is used to base the timeout on _active_ time rather than _elapsed_ time. + * Otherwise, `timeout` falls back to {@link setTimeout}. + * + * @group Static Methods + */ + static timeout(milliseconds, cause) { + const promise = new CancellablePromise(() => { + }); + if (AbortSignal && typeof AbortSignal === "function" && AbortSignal.timeout && typeof AbortSignal.timeout === "function") { + AbortSignal.timeout(milliseconds).addEventListener("abort", () => void promise.cancel(cause)); + } else { + setTimeout(() => void promise.cancel(cause), milliseconds); + } + return promise; + } + static sleep(milliseconds, value) { + return new CancellablePromise((resolve) => { + setTimeout(() => resolve(value), milliseconds); + }); + } + /** + * Creates a new rejected CancellablePromise for the provided reason. + * + * @group Static Methods + */ + static reject(reason) { + return new CancellablePromise((_, reject) => reject(reason)); + } + static resolve(value) { + if (value instanceof CancellablePromise) { + return value; + } + return new CancellablePromise((resolve) => resolve(value)); + } + /** + * Creates a new CancellablePromise and returns it in an object, along with its resolve and reject functions + * and a getter/setter for the cancellation callback. + * + * This method is polyfilled, hence available in every OS/webview version. + * + * @group Static Methods + */ + static withResolvers() { + let result = { oncancelled: null }; + result.promise = new CancellablePromise((resolve, reject) => { + result.resolve = resolve; + result.reject = reject; + }, (cause) => { + var _a2; + (_a2 = result.oncancelled) === null || _a2 === void 0 ? void 0 : _a2.call(result, cause); + }); + return result; + } +} +function cancellerFor(promise, state) { + let cancellationPromise = void 0; + return (reason) => { + if (!state.settled) { + state.settled = true; + state.reason = reason; + promise.reject(reason); + void Promise.prototype.then.call(promise.promise, void 0, (err) => { + if (err !== reason) { + throw err; + } + }); + } + if (!state.reason || !promise.oncancelled) { + return; + } + cancellationPromise = new Promise((resolve) => { + try { + resolve(promise.oncancelled(state.reason.cause)); + } catch (err) { + Promise.reject(new CancelledRejectionError(promise.promise, err, "Unhandled exception in oncancelled callback.")); + } + }).catch((reason2) => { + Promise.reject(new CancelledRejectionError(promise.promise, reason2, "Unhandled rejection in oncancelled callback.")); + }); + promise.oncancelled = null; + return cancellationPromise; + }; +} +function resolverFor(promise, state) { + return (value) => { + if (state.resolving) { + return; + } + state.resolving = true; + if (value === promise.promise) { + if (state.settled) { + return; + } + state.settled = true; + promise.reject(new TypeError("A promise cannot be resolved with itself.")); + return; + } + if (value != null && (typeof value === "object" || typeof value === "function")) { + let then; + try { + then = value.then; + } catch (err) { + state.settled = true; + promise.reject(err); + return; + } + if (isCallable(then)) { + try { + let cancel = value.cancel; + if (isCallable(cancel)) { + const oncancelled = (cause) => { + Reflect.apply(cancel, value, [cause]); + }; + if (state.reason) { + void cancellerFor(Object.assign(Object.assign({}, promise), { oncancelled }), state)(state.reason); + } else { + promise.oncancelled = oncancelled; + } + } + } catch (_a2) { + } + const newState = { + root: state.root, + resolving: false, + get settled() { + return this.root.settled; + }, + set settled(value2) { + this.root.settled = value2; + }, + get reason() { + return this.root.reason; + } + }; + const rejector = rejectorFor(promise, newState); + try { + Reflect.apply(then, value, [resolverFor(promise, newState), rejector]); + } catch (err) { + rejector(err); + } + return; + } + } + if (state.settled) { + return; + } + state.settled = true; + promise.resolve(value); + }; +} +function rejectorFor(promise, state) { + return (reason) => { + if (state.resolving) { + return; + } + state.resolving = true; + if (state.settled) { + try { + if (reason instanceof CancelError && state.reason instanceof CancelError && Object.is(reason.cause, state.reason.cause)) { + return; + } + } catch (_a2) { + } + void Promise.reject(new CancelledRejectionError(promise.promise, reason)); + } else { + state.settled = true; + promise.reject(reason); + } + }; +} +function cancelAll(parent, values, cause) { + const results = []; + for (const value of values) { + let cancel; + try { + if (!isCallable(value.then)) { + continue; + } + cancel = value.cancel; + if (!isCallable(cancel)) { + continue; + } + } catch (_a2) { + continue; + } + let result; + try { + result = Reflect.apply(cancel, value, [cause]); + } catch (err) { + Promise.reject(new CancelledRejectionError(parent, err, "Unhandled exception in cancel method.")); + continue; + } + if (!result) { + continue; + } + results.push((result instanceof Promise ? result : Promise.resolve(result)).catch((reason) => { + Promise.reject(new CancelledRejectionError(parent, reason, "Unhandled rejection in cancel method.")); + })); + } + return Promise.all(results); +} +function identity(x) { + return x; +} +function thrower(reason) { + throw reason; +} +function errorMessage(err) { + try { + if (err instanceof Error || typeof err !== "object" || err.toString !== Object.prototype.toString) { + return "" + err; + } + } catch (_a2) { + } + try { + return JSON.stringify(err); + } catch (_b) { + } + try { + return Object.prototype.toString.call(err); + } catch (_c) { + } + return ""; +} +function currentBarrier(promise) { + var _a2; + let pwr = (_a2 = promise[barrierSym]) !== null && _a2 !== void 0 ? _a2 : {}; + if (!("promise" in pwr)) { + Object.assign(pwr, promiseWithResolvers()); + } + if (promise[barrierSym] == null) { + pwr.resolve(); + promise[barrierSym] = pwr; + } + return pwr.promise; +} +let promiseWithResolvers = Promise.withResolvers; +if (promiseWithResolvers && typeof promiseWithResolvers === "function") { + promiseWithResolvers = promiseWithResolvers.bind(Promise); +} else { + promiseWithResolvers = function() { + let resolve; + let reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; + }; +} +window._wails = window._wails || {}; +window._wails.callResultHandler = resultHandler; +window._wails.callErrorHandler = errorHandler; +const call$1 = newRuntimeCaller(objectNames.Call); +const cancelCall = newRuntimeCaller(objectNames.CancelCall); +const callResponses = /* @__PURE__ */ new Map(); +const CallBinding = 0; +const CancelMethod = 0; +class RuntimeError extends Error { + /** + * Constructs a new RuntimeError instance. + * @param message - The error message. + * @param options - Options to be forwarded to the Error constructor. + */ + constructor(message, options) { + super(message, options); + this.name = "RuntimeError"; + } +} +function resultHandler(id, data, isJSON) { + const resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + if (!data) { + resolvers.resolve(void 0); + } else if (!isJSON) { + resolvers.resolve(data); + } else { + try { + resolvers.resolve(JSON.parse(data)); + } catch (err) { + resolvers.reject(new TypeError("could not parse result: " + err.message, { cause: err })); + } + } +} +function errorHandler(id, data, isJSON) { + const resolvers = getAndDeleteResponse(id); + if (!resolvers) { + return; + } + if (!isJSON) { + resolvers.reject(new Error(data)); + } else { + let error; + try { + error = JSON.parse(data); + } catch (err) { + resolvers.reject(new TypeError("could not parse error: " + err.message, { cause: err })); + return; + } + let options = {}; + if (error.cause) { + options.cause = error.cause; + } + let exception; + switch (error.kind) { + case "ReferenceError": + exception = new ReferenceError(error.message, options); + break; + case "TypeError": + exception = new TypeError(error.message, options); + break; + case "RuntimeError": + exception = new RuntimeError(error.message, options); + break; + default: + exception = new Error(error.message, options); + break; + } + resolvers.reject(exception); + } +} +function getAndDeleteResponse(id) { + const response = callResponses.get(id); + callResponses.delete(id); + return response; +} +function generateID() { + let result; + do { + result = nanoid(); + } while (callResponses.has(result)); + return result; +} +function Call(options) { + const id = generateID(); + const result = CancellablePromise.withResolvers(); + callResponses.set(id, { resolve: result.resolve, reject: result.reject }); + const request = call$1(CallBinding, Object.assign({ "call-id": id }, options)); + let running = false; + request.then(() => { + running = true; + }, (err) => { + callResponses.delete(id); + result.reject(err); + }); + const cancel = () => { + callResponses.delete(id); + return cancelCall(CancelMethod, { "call-id": id }).catch((err) => { + console.error("Error while requesting binding call cancellation:", err); + }); + }; + result.oncancelled = () => { + if (running) { + return cancel(); + } else { + return request.then(cancel); + } + }; + return result.promise; +} +function ByID(methodID, ...args) { + return Call({ methodID, args }); +} +const eventListeners = /* @__PURE__ */ new Map(); +class Listener { + constructor(eventName, callback, maxCallbacks) { + this.eventName = eventName; + this.callback = callback; + this.maxCallbacks = maxCallbacks || -1; + } + dispatch(data) { + try { + this.callback(data); + } catch (err) { + console.error(err); + } + if (this.maxCallbacks === -1) + return false; + this.maxCallbacks -= 1; + return this.maxCallbacks === 0; + } +} +function listenerOff(listener) { + let listeners = eventListeners.get(listener.eventName); + if (!listeners) { + return; + } + listeners = listeners.filter((l) => l !== listener); + if (listeners.length === 0) { + eventListeners.delete(listener.eventName); + } else { + eventListeners.set(listener.eventName, listeners); + } +} +window._wails = window._wails || {}; +window._wails.dispatchWailsEvent = dispatchWailsEvent; +const call = newRuntimeCaller(objectNames.Events); +const EmitMethod = 0; +class WailsEvent { + constructor(name, data = null) { + this.name = name; + this.data = data; + } +} +function dispatchWailsEvent(event) { + let listeners = eventListeners.get(event.name); + if (!listeners) { + return; + } + let wailsEvent = new WailsEvent(event.name, event.data); + if ("sender" in event) { + wailsEvent.sender = event.sender; + } + listeners = listeners.filter((listener) => !listener.dispatch(wailsEvent)); + if (listeners.length === 0) { + eventListeners.delete(event.name); + } else { + eventListeners.set(event.name, listeners); + } +} +function OnMultiple(eventName, callback, maxCallbacks) { + let listeners = eventListeners.get(eventName) || []; + const thisListener = new Listener(eventName, callback, maxCallbacks); + listeners.push(thisListener); + eventListeners.set(eventName, listeners); + return () => listenerOff(thisListener); +} +function On(eventName, callback) { + return OnMultiple(eventName, callback, -1); +} +function Emit(event) { + return call(EmitMethod, event); +} +window._wails = window._wails || {}; +window._wails.invoke = invoke; +invoke("wails:runtime:ready"); +function RemoveBadge() { + return ByID(2752757297); +} +function SetBadge(label) { + return ByID(1717705661, label); +} +const setButton = document.getElementById("set"); +const removeButton = document.getElementById("remove"); +const setButtonUsingGo = document.getElementById("set-go"); +const removeButtonUsingGo = document.getElementById("remove-go"); +const labelElement = document.getElementById("label"); +const timeElement = document.getElementById("time"); +setButton.addEventListener("click", () => { + let label = labelElement.value; + SetBadge(label); +}); +removeButton.addEventListener("click", () => { + RemoveBadge(); +}); +setButtonUsingGo.addEventListener("click", () => { + let label = labelElement.value; + void Emit({ + name: "set:badge", + data: label + }); +}); +removeButtonUsingGo.addEventListener("click", () => { + void Emit({ name: "remove:badge", data: null }); +}); +On("time", (time) => { + timeElement.innerText = time.data; +}); diff --git a/v3/examples/badge/frontend/dist/assets/index-djydTGr9.js b/v3/examples/badge/frontend/dist/assets/index-djydTGr9.js deleted file mode 100644 index 130eddeb5..000000000 --- a/v3/examples/badge/frontend/dist/assets/index-djydTGr9.js +++ /dev/null @@ -1,6 +0,0 @@ -(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function r(o){if(o.ep)return;o.ep=!0;const i=n(o);fetch(o.href,i)}})();const ce="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function $(t=21){let e="",n=t|0;for(;n--;)e+=ce[Math.random()*64|0];return e}const ae=window.location.origin+"/wails/runtime",C=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let ue=$();function z(t,e=""){return function(n,r=null){return de(t,n,e,r)}}async function de(t,e,n,r){var o,i;let s=new URL(ae);s.searchParams.append("object",t.toString()),s.searchParams.append("method",e.toString()),r&&s.searchParams.append("args",JSON.stringify(r));let c={"x-wails-client-id":ue};n&&(c["x-wails-window-name"]=n);let l=await fetch(s,{headers:c});if(!l.ok)throw new Error(await l.text());return((i=(o=l.headers.get("Content-Type"))===null||o===void 0?void 0:o.indexOf("application/json"))!==null&&i!==void 0?i:-1)!==-1?l.json():l.text()}z(C.System);const P=function(){var t,e,n,r,o;try{if(!((e=(t=window.chrome)===null||t===void 0?void 0:t.webview)===null||e===void 0)&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((o=(r=(n=window.webkit)===null||n===void 0?void 0:n.messageHandlers)===null||r===void 0?void 0:r.external)===null||o===void 0)&&o.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(` -%c⚠️ Browser Environment Detected %c - -%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. -More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development -`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function R(t){P==null||P(t)}function Q(){return window._wails.environment.OS==="windows"}function fe(){return!!window._wails.environment.Debug}function we(){return new MouseEvent("mousedown").buttons===0}function Z(t){var e;return t.target instanceof HTMLElement?t.target:!(t.target instanceof HTMLElement)&&t.target instanceof Node&&(e=t.target.parentElement)!==null&&e!==void 0?e:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",ge);const pe=z(C.ContextMenu),me=0;function he(t,e,n,r){pe(me,{id:t,x:e,y:n,data:r})}function ge(t){const e=Z(t),n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(n){t.preventDefault();const r=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");he(n,t.clientX,t.clientY,r)}else ye(t,e)}function ye(t,e){if(fe())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return}if(e.isContentEditable)return;const n=window.getSelection(),r=n&&n.toString().length>0;if(r)for(let o=0;o{F=t,F||(v=E=!1,u())};window.addEventListener("mousedown",N,{capture:!0});window.addEventListener("mousemove",N,{capture:!0});window.addEventListener("mouseup",N,{capture:!0});for(const t of["click","contextmenu","dblclick"])window.addEventListener(t,be,{capture:!0});function be(t){(S||E)&&(t.stopImmediatePropagation(),t.stopPropagation(),t.preventDefault())}const B=0,ve=1,D=2;function N(t){let e,n=t.buttons;switch(t.type){case"mousedown":e=B,H||(n=h|1<"u"||typeof e=="object"))try{var n=L.call(e);return(n===Le||n===Re||n===Te||n===ze)&&e("")==null}catch{}return!1})}function He(t){if(Y(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;try{y(t,null,_)}catch(e){if(e!==O)return!1}return!X(t)&&U(t)}function Be(t){if(Y(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;if(ke)return U(t);if(X(t))return!1;var e=L.call(t);return e!==Me&&e!==Oe&&!/^\[object HTML/.test(e)?!1:U(t)}const m=y?He:Be;var I;class W extends Error{constructor(e,n){super(e,n),this.name="CancelError"}}class x extends Error{constructor(e,n,r){super((r??"Unhandled rejection in cancelled promise.")+" Reason: "+De(n),{cause:n}),this.promise=e,this.name="CancelledRejectionError"}}const f=Symbol("barrier"),J=Symbol("cancelImpl"),V=(I=Symbol.species)!==null&&I!==void 0?I:Symbol("speciesPolyfill");class a extends Promise{constructor(e,n){let r,o;if(super((l,d)=>{r=l,o=d}),this.constructor[V]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let i={promise:this,resolve:r,reject:o,get oncancelled(){return n??null},set oncancelled(l){n=l??void 0}};const s={get root(){return s},resolving:!1,settled:!1};Object.defineProperties(this,{[f]:{configurable:!1,enumerable:!1,writable:!0,value:null},[J]:{configurable:!1,enumerable:!1,writable:!1,value:te(i,s)}});const c=re(i,s);try{e(ne(i,s),c)}catch(l){s.resolving?console.log("Unhandled exception in CancellablePromise executor.",l):c(l)}}cancel(e){return new a(n=>{Promise.all([this[J](new W("Promise cancelled.",{cause:e})),Ie(this)]).then(()=>n(),()=>n())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,n,r){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(m(e)||(e=q),m(n)||(n=K),e===q&&n==K)return new a(i=>i(this));const o={};return this[f]=o,new a((i,s)=>{super.then(c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(e(c))}catch(d){s(d)}},c=>{var l;this[f]===o&&(this[f]=null),(l=o.resolve)===null||l===void 0||l.call(o);try{i(n(c))}catch(d){s(d)}})},async i=>{try{return r==null?void 0:r(i)}finally{await this.cancel(i)}})}catch(e,n){return this.then(void 0,e,n)}finally(e,n){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return m(e)?this.then(r=>a.resolve(e()).then(()=>r),r=>a.resolve(e()).then(()=>{throw r}),n):this.then(e,e,n)}static get[V](){return Promise}static all(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.all(n).then(o,i)},o=>M(r,n,o));return r}static allSettled(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.allSettled(n).then(o,i)},o=>M(r,n,o));return r}static any(e){let n=Array.from(e);const r=n.length===0?a.resolve(n):new a((o,i)=>{Promise.any(n).then(o,i)},o=>M(r,n,o));return r}static race(e){let n=Array.from(e);const r=new a((o,i)=>{Promise.race(n).then(o,i)},o=>M(r,n,o));return r}static cancel(e){const n=new a(()=>{});return n.cancel(e),n}static timeout(e,n){const r=new a(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void r.cancel(n)):setTimeout(()=>void r.cancel(n),e),r}static sleep(e,n){return new a(r=>{setTimeout(()=>r(n),e)})}static reject(e){return new a((n,r)=>r(e))}static resolve(e){return e instanceof a?e:new a(n=>n(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new a((n,r)=>{e.resolve=n,e.reject=r},n=>{var r;(r=e.oncancelled)===null||r===void 0||r.call(e,n)}),e}}function te(t,e){let n;return r=>{if(e.settled||(e.settled=!0,e.reason=r,t.reject(r),Promise.prototype.then.call(t.promise,void 0,o=>{if(o!==r)throw o})),!(!e.reason||!t.oncancelled))return n=new Promise(o=>{try{o(t.oncancelled(e.reason.cause))}catch(i){Promise.reject(new x(t.promise,i,"Unhandled exception in oncancelled callback."))}}).catch(o=>{Promise.reject(new x(t.promise,o,"Unhandled rejection in oncancelled callback."))}),t.oncancelled=null,n}}function ne(t,e){return n=>{if(!e.resolving){if(e.resolving=!0,n===t.promise){if(e.settled)return;e.settled=!0,t.reject(new TypeError("A promise cannot be resolved with itself."));return}if(n!=null&&(typeof n=="object"||typeof n=="function")){let r;try{r=n.then}catch(o){e.settled=!0,t.reject(o);return}if(m(r)){try{let s=n.cancel;if(m(s)){const c=l=>{Reflect.apply(s,n,[l])};e.reason?te(Object.assign(Object.assign({},t),{oncancelled:c}),e)(e.reason):t.oncancelled=c}}catch{}const o={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(s){this.root.settled=s},get reason(){return this.root.reason}},i=re(t,o);try{Reflect.apply(r,n,[ne(t,o),i])}catch(s){i(s)}return}}e.settled||(e.settled=!0,t.resolve(n))}}}function re(t,e){return n=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(n instanceof W&&e.reason instanceof W&&Object.is(n.cause,e.reason.cause))return}catch{}Promise.reject(new x(t.promise,n))}else e.settled=!0,t.reject(n)}}function M(t,e,n){const r=[];for(const o of e){let i;try{if(!m(o.then)||(i=o.cancel,!m(i)))continue}catch{continue}let s;try{s=Reflect.apply(i,o,[n])}catch(c){Promise.reject(new x(t,c,"Unhandled exception in cancel method."));continue}s&&r.push((s instanceof Promise?s:Promise.resolve(s)).catch(c=>{Promise.reject(new x(t,c,"Unhandled rejection in cancel method."))}))}return Promise.all(r)}function q(t){return t}function K(t){throw t}function De(t){try{if(t instanceof Error||typeof t!="object"||t.toString!==Object.prototype.toString)return""+t}catch{}try{return JSON.stringify(t)}catch{}try{return Object.prototype.toString.call(t)}catch{}return""}function Ie(t){var e;let n=(e=t[f])!==null&&e!==void 0?e:{};return"promise"in n||Object.assign(n,g()),t[f]==null&&(n.resolve(),t[f]=n),n.promise}let g=Promise.withResolvers;g&&typeof g=="function"?g=g.bind(Promise):g=function(){let t,e;return{promise:new Promise((r,o)=>{t=r,e=o}),resolve:t,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Xe;window._wails.callErrorHandler=Ye;const Fe=z(C.Call),_e=z(C.CancelCall),b=new Map,Ue=0,We=0;class Ne extends Error{constructor(e,n){super(e,n),this.name="RuntimeError"}}function Xe(t,e,n){const r=oe(t);if(r)if(!e)r.resolve(void 0);else if(!n)r.resolve(e);else try{r.resolve(JSON.parse(e))}catch(o){r.reject(new TypeError("could not parse result: "+o.message,{cause:o}))}}function Ye(t,e,n){const r=oe(t);if(r)if(!n)r.reject(new Error(e));else{let o;try{o=JSON.parse(e)}catch(c){r.reject(new TypeError("could not parse error: "+c.message,{cause:c}));return}let i={};o.cause&&(i.cause=o.cause);let s;switch(o.kind){case"ReferenceError":s=new ReferenceError(o.message,i);break;case"TypeError":s=new TypeError(o.message,i);break;case"RuntimeError":s=new Ne(o.message,i);break;default:s=new Error(o.message,i);break}r.reject(s)}}function oe(t){const e=b.get(t);return b.delete(t),e}function Ge(){let t;do t=$();while(b.has(t));return t}function Je(t){const e=Ge(),n=a.withResolvers();b.set(e,{resolve:n.resolve,reject:n.reject});const r=Fe(Ue,Object.assign({"call-id":e},t));let o=!1;r.then(()=>{o=!0},s=>{b.delete(e),n.reject(s)});const i=()=>(b.delete(e),_e(We,{"call-id":e}).catch(s=>{console.error("Error while requesting binding call cancellation:",s)}));return n.oncancelled=()=>o?i():r.then(i),n.promise}function ie(t,...e){return Je({methodID:t,args:e})}const w=new Map;class Ve{constructor(e,n,r){this.eventName=e,this.callback=n,this.maxCallbacks=r||-1}dispatch(e){try{this.callback(e)}catch(n){console.error(n)}return this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0)}}function qe(t){let e=w.get(t.eventName);e&&(e=e.filter(n=>n!==t),e.length===0?w.delete(t.eventName):w.set(t.eventName,e))}window._wails=window._wails||{};window._wails.dispatchWailsEvent=Ze;const Ke=z(C.Events),$e=0;class Qe{constructor(e,n=null){this.name=e,this.data=n}}function Ze(t){let e=w.get(t.name);if(!e)return;let n=new Qe(t.name,t.data);"sender"in t&&(n.sender=t.sender),e=e.filter(r=>!r.dispatch(n)),e.length===0?w.delete(t.name):w.set(t.name,e)}function et(t,e,n){let r=w.get(t)||[];const o=new Ve(t,e,n);return r.push(o),w.set(t,r),()=>qe(o)}function tt(t,e){return et(t,e,-1)}function se(t){return Ke($e,t)}window._wails=window._wails||{};window._wails.invoke=R;R("wails:runtime:ready");function nt(){return ie(2374916939)}function rt(t){return ie(784276339,t)}const ot=document.getElementById("set"),it=document.getElementById("remove"),st=document.getElementById("set-go"),lt=document.getElementById("remove-go"),le=document.getElementById("label"),ct=document.getElementById("time");ot.addEventListener("click",()=>{let t=le.value;rt(t)});it.addEventListener("click",()=>{nt()});st.addEventListener("click",()=>{let t=le.value;se({name:"set:badge",data:t})});lt.addEventListener("click",()=>{se({name:"remove:badge",data:null})});tt("time",t=>{ct.innerText=t.data}); diff --git a/v3/examples/badge/frontend/dist/index.html b/v3/examples/badge/frontend/dist/index.html index d5b44d109..b8ca49f06 100644 --- a/v3/examples/badge/frontend/dist/index.html +++ b/v3/examples/badge/frontend/dist/index.html @@ -6,7 +6,7 @@ Wails App - +
diff --git a/v3/examples/badge/frontend/src/main.ts b/v3/examples/badge/frontend/src/main.ts index 934741aba..7f4ba9bfb 100644 --- a/v3/examples/badge/frontend/src/main.ts +++ b/v3/examples/badge/frontend/src/main.ts @@ -1,5 +1,5 @@ import {Events} from "@wailsio/runtime"; -import {SetBadge, RemoveBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/badge/badgeservice"; +import {SetBadge, RemoveBadge} from "../bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice"; const setButton = document.getElementById('set')! as HTMLButtonElement; const removeButton = document.getElementById('remove')! as HTMLButtonElement; diff --git a/v3/examples/badge/main.go b/v3/examples/badge/main.go index 9de2944c0..f97f3924e 100644 --- a/v3/examples/badge/main.go +++ b/v3/examples/badge/main.go @@ -7,7 +7,7 @@ import ( "time" "github.com/wailsapp/wails/v3/pkg/application" - "github.com/wailsapp/wails/v3/pkg/services/badge" + "github.com/wailsapp/wails/v3/pkg/services/dock" ) // Wails uses Go's `embed` package to embed the frontend files into the binary. @@ -28,13 +28,13 @@ func main() { // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. // 'Mac' options tailor the application when running an macOS. - badgeService := badge.New() + dockService := dock.New() app := application.New(application.Options{ Name: "badge", Description: "A demo of using raw HTML & CSS", Services: []application.Service{ - application.NewService(badgeService), + application.NewService(dockService), }, Assets: application.AssetOptions{ Handler: application.AssetFileServerFS(assets), @@ -62,7 +62,7 @@ func main() { // Store cleanup functions for proper resource management removeBadgeHandler := app.Event.On("remove:badge", func(event *application.CustomEvent) { - err := badgeService.RemoveBadge() + err := dockService.RemoveBadge() if err != nil { log.Fatal(err) } @@ -70,12 +70,12 @@ func main() { setBadgeHandler := app.Event.On("set:badge", func(event *application.CustomEvent) { text := event.Data.(string) - err := badgeService.SetBadge(text) + err := dockService.SetBadge(text) if err != nil { log.Fatal(err) } }) - + // Note: In a production application, you would call these cleanup functions // when the handlers are no longer needed, e.g., during shutdown: // defer removeBadgeHandler() diff --git a/v3/examples/dock/README.md b/v3/examples/dock/README.md new file mode 100644 index 000000000..ad12c3f40 --- /dev/null +++ b/v3/examples/dock/README.md @@ -0,0 +1,59 @@ +# Welcome to Your New Wails3 Project! + +Congratulations on generating your Wails3 application! This README will guide you through the next steps to get your project up and running. + +## Getting Started + +1. Navigate to your project directory in the terminal. + +2. To run your application in development mode, use the following command: + + ``` + wails3 dev + ``` + + This will start your application and enable hot-reloading for both frontend and backend changes. + +3. To build your application for production, use: + + ``` + wails3 build + ``` + + This will create a production-ready executable in the `build` directory. + +## Exploring Wails3 Features + +Now that you have your project set up, it's time to explore the features that Wails3 offers: + +1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications. + +2. **Run an example**: To run any of the examples, navigate to the example's directory and use: + + ``` + go run . + ``` + + Note: Some examples may be under development during the alpha phase. + +3. **Explore the documentation**: Visit the [Wails3 documentation](https://v3.wails.io/) for in-depth guides and API references. + +4. **Join the community**: Have questions or want to share your progress? Join the [Wails Discord](https://discord.gg/JDdSxwjhGf) or visit the [Wails discussions on GitHub](https://github.com/wailsapp/wails/discussions). + +## Project Structure + +Take a moment to familiarize yourself with your project structure: + +- `frontend/`: Contains your frontend code (HTML, CSS, JavaScript/TypeScript) +- `main.go`: The entry point of your Go backend +- `app.go`: Define your application structure and methods here +- `wails.json`: Configuration file for your Wails project + +## Next Steps + +1. Modify the frontend in the `frontend/` directory to create your desired UI. +2. Add backend functionality in `main.go`. +3. Use `wails3 dev` to see your changes in real-time. +4. When ready, build your application with `wails3 build`. + +Happy coding with Wails3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community. diff --git a/v3/examples/dock/Taskfile.yml b/v3/examples/dock/Taskfile.yml new file mode 100644 index 000000000..b80b6614a --- /dev/null +++ b/v3/examples/dock/Taskfile.yml @@ -0,0 +1,34 @@ +version: '3' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +vars: + APP_NAME: "dock" + BIN_DIR: "bin" + VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{OS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{OS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{OS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}} + diff --git a/v3/examples/dock/build/Taskfile.yml b/v3/examples/dock/build/Taskfile.yml new file mode 100644 index 000000000..5f3517efc --- /dev/null +++ b/v3/examples/dock/build/Taskfile.yml @@ -0,0 +1,86 @@ +version: '3' + +tasks: + go:mod:tidy: + summary: Runs `go mod tidy` + internal: true + cmds: + - go mod tidy + + install:frontend:deps: + summary: Install frontend dependencies + dir: frontend + sources: + - package.json + - package-lock.json + generates: + - node_modules/* + preconditions: + - sh: npm version + msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/" + cmds: + - npm install + + build:frontend: + label: build:frontend (PRODUCTION={{.PRODUCTION}}) + summary: Build the frontend project + dir: frontend + sources: + - "**/*" + generates: + - dist/**/* + deps: + - task: install:frontend:deps + - task: generate:bindings + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + cmds: + - npm run {{.BUILD_COMMAND}} -q + env: + PRODUCTION: '{{.PRODUCTION | default "false"}}' + vars: + BUILD_COMMAND: '{{if eq .PRODUCTION "true"}}build{{else}}build:dev{{end}}' + + + generate:bindings: + label: generate:bindings (BUILD_FLAGS={{.BUILD_FLAGS}}) + summary: Generates bindings for the frontend + deps: + - task: go:mod:tidy + sources: + - "**/*.[jt]s" + - exclude: frontend/**/* + - frontend/bindings/**/* # Rerun when switching between dev/production mode causes changes in output + - "**/*.go" + - go.mod + - go.sum + generates: + - frontend/bindings/**/* + cmds: + - wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true -ts + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` files from an image + dir: build + sources: + - "appicon.png" + generates: + - "darwin/icons.icns" + - "windows/icon.ico" + cmds: + - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico + + dev:frontend: + summary: Runs the frontend in development mode + dir: frontend + deps: + - task: install:frontend:deps + cmds: + - npm run dev -- --port {{.VITE_PORT}} --strictPort + + update:build-assets: + summary: Updates the build assets + dir: build + cmds: + - wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir . diff --git a/v3/examples/dock/build/appicon.png b/v3/examples/dock/build/appicon.png new file mode 100644 index 000000000..63617fe4f Binary files /dev/null and b/v3/examples/dock/build/appicon.png differ diff --git a/v3/examples/dock/build/config.yml b/v3/examples/dock/build/config.yml new file mode 100644 index 000000000..aaa3240fb --- /dev/null +++ b/v3/examples/dock/build/config.yml @@ -0,0 +1,63 @@ +# This file contains the configuration for this project. +# When you update `info` or `fileAssociations`, run `wails3 task common:update:build-assets` to update the assets. +# Note that this will overwrite any changes you have made to the assets. +version: '3' + +# This information is used to generate the build assets. +info: + companyName: "My Company" # The name of the company + productName: "My Product" # The name of the application + productIdentifier: "com.mycompany.myproduct" # The unique product identifier + description: "A program that does X" # The application description + copyright: "(c) 2025, My Company" # Copyright text + comments: "Some Product Comments" # Comments + version: "0.0.1" # The application version + +# Dev mode configuration +dev_mode: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + watched_extension: + - "*.go" + git_ignore: true + executes: + - cmd: wails3 task common:install:frontend:deps + type: once + - cmd: wails3 task common:dev:frontend + type: background + - cmd: go mod tidy + type: blocking + - cmd: wails3 task build + type: blocking + - cmd: wails3 task run + type: primary + +# File Associations +# More information at: https://v3.wails.io/noit/done/yet +fileAssociations: +# - ext: wails +# name: Wails +# description: Wails Application File +# iconName: wailsFileIcon +# role: Editor +# - ext: jpg +# name: JPEG +# description: Image File +# iconName: jpegFileIcon +# role: Editor +# mimeType: image/jpeg # (optional) + +# Other data +other: + - name: My Other Data \ No newline at end of file diff --git a/v3/examples/dock/build/darwin/Info.dev.plist b/v3/examples/dock/build/darwin/Info.dev.plist new file mode 100644 index 000000000..58d7f65e7 --- /dev/null +++ b/v3/examples/dock/build/darwin/Info.dev.plist @@ -0,0 +1,32 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + dock + CFBundleIdentifier + com.wails.dock + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + \ No newline at end of file diff --git a/v3/examples/dock/build/darwin/Info.plist b/v3/examples/dock/build/darwin/Info.plist new file mode 100644 index 000000000..aff7a2812 --- /dev/null +++ b/v3/examples/dock/build/darwin/Info.plist @@ -0,0 +1,27 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + dock + CFBundleIdentifier + com.wails.dock + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + LSMinimumSystemVersion + 10.15.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © now, My Company + + \ No newline at end of file diff --git a/v3/examples/dock/build/darwin/Taskfile.yml b/v3/examples/dock/build/darwin/Taskfile.yml new file mode 100644 index 000000000..f0791fea9 --- /dev/null +++ b/v3/examples/dock/build/darwin/Taskfile.yml @@ -0,0 +1,81 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Creates a production build of the application + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.OUTPUT}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}' + OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}' + env: + GOOS: darwin + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + CGO_CFLAGS: "-mmacosx-version-min=10.15" + CGO_LDFLAGS: "-mmacosx-version-min=10.15" + MACOSX_DEPLOYMENT_TARGET: "10.15" + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + build:universal: + summary: Builds darwin universal binary (arm64 + amd64) + deps: + - task: build + vars: + ARCH: amd64 + OUTPUT: "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" + - task: build + vars: + ARCH: arm64 + OUTPUT: "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + cmds: + - lipo -create -output "{{.BIN_DIR}}/{{.APP_NAME}}" "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + - rm "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + + package: + summary: Packages a production build of the application into a `.app` bundle + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: create:app:bundle + + package:universal: + summary: Packages darwin universal binary (arm64 + amd64) + deps: + - task: build:universal + cmds: + - task: create:app:bundle + + + create:app:bundle: + summary: Creates an `.app` bundle + cmds: + - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/{MacOS,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS + - cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents + - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app + + run: + cmds: + - mkdir -p {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/{MacOS,Resources} + - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources + - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS + - cp build/darwin/Info.dev.plist {{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Info.plist + - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.dev.app + - '{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS/{{.APP_NAME}}' diff --git a/v3/examples/dock/build/darwin/icons.icns b/v3/examples/dock/build/darwin/icons.icns new file mode 100644 index 000000000..1b5bd4c86 Binary files /dev/null and b/v3/examples/dock/build/darwin/icons.icns differ diff --git a/v3/examples/dock/build/linux/Taskfile.yml b/v3/examples/dock/build/linux/Taskfile.yml new file mode 100644 index 000000000..560cc9c92 --- /dev/null +++ b/v3/examples/dock/build/linux/Taskfile.yml @@ -0,0 +1,119 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Linux + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}} + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: linux + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application for Linux + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: create:appimage + - task: create:deb + - task: create:rpm + - task: create:aur + + create:appimage: + summary: Creates an AppImage + dir: build/linux/appimage + deps: + - task: build + vars: + PRODUCTION: "true" + - task: generate:dotdesktop + cmds: + - cp {{.APP_BINARY}} {{.APP_NAME}} + - cp ../../appicon.png appicon.png + - wails3 generate appimage -binary {{.APP_NAME}} -icon {{.ICON}} -desktopfile {{.DESKTOP_FILE}} -outputdir {{.OUTPUT_DIR}} -builddir {{.ROOT_DIR}}/build/linux/appimage/build + vars: + APP_NAME: '{{.APP_NAME}}' + APP_BINARY: '../../../bin/{{.APP_NAME}}' + ICON: '../../appicon.png' + DESKTOP_FILE: '../{{.APP_NAME}}.desktop' + OUTPUT_DIR: '../../../bin' + + create:deb: + summary: Creates a deb package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:deb + + create:rpm: + summary: Creates a rpm package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:rpm + + create:aur: + summary: Creates a arch linux packager package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - task: generate:dotdesktop + - task: generate:aur + + generate:deb: + summary: Creates a deb package + cmds: + - wails3 tool package -name {{.APP_NAME}} -format deb -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin + + generate:rpm: + summary: Creates a rpm package + cmds: + - wails3 tool package -name {{.APP_NAME}} -format rpm -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin + + generate:aur: + summary: Creates a arch linux packager package + cmds: + - wails3 tool package -name {{.APP_NAME}} -format archlinux -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin + + generate:dotdesktop: + summary: Generates a `.desktop` file + dir: build + cmds: + - mkdir -p {{.ROOT_DIR}}/build/linux/appimage + - wails3 generate .desktop -name "{{.APP_NAME}}" -exec "{{.EXEC}}" -icon "{{.ICON}}" -outputfile {{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop -categories "{{.CATEGORIES}}" + vars: + APP_NAME: '{{.APP_NAME}}' + EXEC: '{{.APP_NAME}}' + ICON: 'appicon' + CATEGORIES: 'Development;' + OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' diff --git a/v3/examples/dock/build/linux/appimage/build.sh b/v3/examples/dock/build/linux/appimage/build.sh new file mode 100644 index 000000000..85901c34e --- /dev/null +++ b/v3/examples/dock/build/linux/appimage/build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Copyright (c) 2018-Present Lea Anthony +# SPDX-License-Identifier: MIT + +# Fail script on any error +set -euxo pipefail + +# Define variables +APP_DIR="${APP_NAME}.AppDir" + +# Create AppDir structure +mkdir -p "${APP_DIR}/usr/bin" +cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/" +cp "${ICON_PATH}" "${APP_DIR}/" +cp "${DESKTOP_FILE}" "${APP_DIR}/" + +if [[ $(uname -m) == *x86_64* ]]; then + # Download linuxdeploy and make it executable + wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + chmod +x linuxdeploy-x86_64.AppImage + + # Run linuxdeploy to bundle the application + ./linuxdeploy-x86_64.AppImage --appdir "${APP_DIR}" --output appimage +else + # Download linuxdeploy and make it executable (arm64) + wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-aarch64.AppImage + chmod +x linuxdeploy-aarch64.AppImage + + # Run linuxdeploy to bundle the application (arm64) + ./linuxdeploy-aarch64.AppImage --appdir "${APP_DIR}" --output appimage +fi + +# Rename the generated AppImage +mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage" + diff --git a/v3/examples/dock/build/linux/desktop b/v3/examples/dock/build/linux/desktop new file mode 100644 index 000000000..ddb025903 --- /dev/null +++ b/v3/examples/dock/build/linux/desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Version=1.0 +Name=My Product +Comment=My Product Description +# The Exec line includes %u to pass the URL to the application +Exec=/usr/local/bin/dock %u +Terminal=false +Type=Application +Icon=dock +Categories=Utility; +StartupWMClass=dock + + diff --git a/v3/examples/dock/build/linux/nfpm/nfpm.yaml b/v3/examples/dock/build/linux/nfpm/nfpm.yaml new file mode 100644 index 000000000..6e9be6550 --- /dev/null +++ b/v3/examples/dock/build/linux/nfpm/nfpm.yaml @@ -0,0 +1,73 @@ +# Feel free to remove those if you don't want/need to use them. +# Make sure to check the documentation at https://nfpm.goreleaser.com +# +# The lines below are called `modelines`. See `:help modeline` + +name: "dock" +arch: ${GOARCH} +platform: "linux" +version: "0.1.0" +section: "default" +priority: "extra" +maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}> +description: "My Product Description" +vendor: "My Company" +homepage: "https://wails.io" +license: "MIT" +release: "1" + +contents: + - src: "./bin/dock" + dst: "/usr/local/bin/dock" + - src: "./build/appicon.png" + dst: "/usr/share/icons/hicolor/128x128/apps/dock.png" + - src: "./build/linux/dock.desktop" + dst: "/usr/share/applications/dock.desktop" + +# Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1 +depends: + - libgtk-3-dev + - libwebkit2gtk-4.1-dev + - build-essential + - pkg-config + +# Distribution-specific overrides for different package formats and WebKit versions +overrides: + # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0) + rpm: + depends: + - gtk3-devel + - webkit2gtk3-devel + - gcc-c++ + - pkg-config + + # Arch Linux packages (WebKit 4.1) + archlinux: + depends: + - gtk3 + - webkit2gtk-4.1 + - base-devel + - pkgconf + +# scripts section to ensure desktop database is updated after install +scripts: + postinstall: "./build/linux/nfpm/scripts/postinstall.sh" + # You can also add preremove, postremove if needed + # preremove: "./build/linux/nfpm/scripts/preremove.sh" + # postremove: "./build/linux/nfpm/scripts/postremove.sh" + +# replaces: +# - foobar +# provides: +# - bar +# depends: +# - gtk3 +# - libwebkit2gtk +# recommends: +# - whatever +# suggests: +# - something-else +# conflicts: +# - not-foo +# - not-bar +# changelog: "changelog.yaml" \ No newline at end of file diff --git a/v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh b/v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh new file mode 100644 index 000000000..4bbb815a3 --- /dev/null +++ b/v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# Update desktop database for .desktop file changes +# This makes the application appear in application menus and registers its capabilities. +if command -v update-desktop-database >/dev/null 2>&1; then + echo "Updating desktop database..." + update-desktop-database -q /usr/share/applications +else + echo "Warning: update-desktop-database command not found. Desktop file may not be immediately recognized." >&2 +fi + +# Update MIME database for custom URL schemes (x-scheme-handler) +# This ensures the system knows how to handle your custom protocols. +if command -v update-mime-database >/dev/null 2>&1; then + echo "Updating MIME database..." + update-mime-database -n /usr/share/mime +else + echo "Warning: update-mime-database command not found. Custom URL schemes may not be immediately recognized." >&2 +fi + +exit 0 diff --git a/v3/examples/dock/build/linux/nfpm/scripts/postremove.sh b/v3/examples/dock/build/linux/nfpm/scripts/postremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/dock/build/linux/nfpm/scripts/postremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/dock/build/linux/nfpm/scripts/preinstall.sh b/v3/examples/dock/build/linux/nfpm/scripts/preinstall.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/dock/build/linux/nfpm/scripts/preinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/dock/build/linux/nfpm/scripts/preremove.sh b/v3/examples/dock/build/linux/nfpm/scripts/preremove.sh new file mode 100644 index 000000000..a9bf588e2 --- /dev/null +++ b/v3/examples/dock/build/linux/nfpm/scripts/preremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/v3/examples/dock/build/windows/Taskfile.yml b/v3/examples/dock/build/windows/Taskfile.yml new file mode 100644 index 000000000..19f137616 --- /dev/null +++ b/v3/examples/dock/build/windows/Taskfile.yml @@ -0,0 +1,98 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +tasks: + build: + summary: Builds the application for Windows + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + PRODUCTION: + ref: .PRODUCTION + - task: common:generate:icons + cmds: + - task: generate:syso + - go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/{{.APP_NAME}}.exe + - cmd: powershell Remove-item *.syso + platforms: [windows] + - cmd: rm -f *.syso + platforms: [linux, darwin] + vars: + BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{else}}-buildvcs=false -gcflags=all="-l"{{end}}' + env: + GOOS: windows + CGO_ENABLED: 0 + GOARCH: '{{.ARCH | default ARCH}}' + PRODUCTION: '{{.PRODUCTION | default "false"}}' + + package: + summary: Packages a production build of the application + cmds: + - |- + if [ "{{.FORMAT | default "nsis"}}" = "msix" ]; then + task: create:msix:package + else + task: create:nsis:installer + fi + vars: + FORMAT: '{{.FORMAT | default "nsis"}}' + + generate:syso: + summary: Generates Windows `.syso` file + dir: build + cmds: + - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso + vars: + ARCH: '{{.ARCH | default ARCH}}' + + create:nsis:installer: + summary: Creates an NSIS installer + dir: build/windows/nsis + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + # Create the Microsoft WebView2 bootstrapper if it doesn't exist + - wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis" + - makensis -DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" project.nsi + vars: + ARCH: '{{.ARCH | default ARCH}}' + ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + + create:msix:package: + summary: Creates an MSIX package + deps: + - task: build + vars: + PRODUCTION: "true" + cmds: + - |- + wails3 tool msix \ + --config "{{.ROOT_DIR}}/wails.json" \ + --name "{{.APP_NAME}}" \ + --executable "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" \ + --arch "{{.ARCH}}" \ + --out "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}-{{.ARCH}}.msix" \ + {{if .CERT_PATH}}--cert "{{.CERT_PATH}}"{{end}} \ + {{if .PUBLISHER}}--publisher "{{.PUBLISHER}}"{{end}} \ + {{if .USE_MSIX_TOOL}}--use-msix-tool{{else}}--use-makeappx{{end}} + vars: + ARCH: '{{.ARCH | default ARCH}}' + CERT_PATH: '{{.CERT_PATH | default ""}}' + PUBLISHER: '{{.PUBLISHER | default ""}}' + USE_MSIX_TOOL: '{{.USE_MSIX_TOOL | default "false"}}' + + install:msix:tools: + summary: Installs tools required for MSIX packaging + cmds: + - wails3 tool msix-install-tools + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}.exe' diff --git a/v3/examples/dock/build/windows/icon.ico b/v3/examples/dock/build/windows/icon.ico new file mode 100644 index 000000000..bfa0690b7 Binary files /dev/null and b/v3/examples/dock/build/windows/icon.ico differ diff --git a/v3/examples/dock/build/windows/info.json b/v3/examples/dock/build/windows/info.json new file mode 100644 index 000000000..850b2b5b0 --- /dev/null +++ b/v3/examples/dock/build/windows/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "0.1.0" + }, + "info": { + "0000": { + "ProductVersion": "0.1.0", + "CompanyName": "My Company", + "FileDescription": "My Product Description", + "LegalCopyright": "© now, My Company", + "ProductName": "My Product", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/v3/examples/dock/build/windows/msix/app_manifest.xml b/v3/examples/dock/build/windows/msix/app_manifest.xml new file mode 100644 index 000000000..8eabe1a03 --- /dev/null +++ b/v3/examples/dock/build/windows/msix/app_manifest.xml @@ -0,0 +1,52 @@ + + + + + + + My Product + My Company + My Product Description + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v3/examples/dock/build/windows/msix/template.xml b/v3/examples/dock/build/windows/msix/template.xml new file mode 100644 index 000000000..2824a2932 --- /dev/null +++ b/v3/examples/dock/build/windows/msix/template.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + false + My Product + My Company + My Product Description + Assets\AppIcon.png + + + + + + + diff --git a/v3/examples/dock/build/windows/nsis/project.nsi b/v3/examples/dock/build/windows/nsis/project.nsi new file mode 100644 index 000000000..b992809bc --- /dev/null +++ b/v3/examples/dock/build/windows/nsis/project.nsi @@ -0,0 +1,112 @@ +Unicode true + +#### +## Please note: Template replacements don't work in this file. They are provided with default defines like +## mentioned underneath. +## If the keyword is not defined, "wails_tools.nsh" will populate them. +## If they are defined here, "wails_tools.nsh" will not touch them. This allows you to use this project.nsi manually +## from outside of Wails for debugging and development of the installer. +## +## For development first make a wails nsis build to populate the "wails_tools.nsh": +## > wails build --target windows/amd64 --nsis +## Then you can call makensis on this file with specifying the path to your binary: +## For a AMD64 only installer: +## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app.exe +## For a ARM64 only installer: +## > makensis -DARG_WAILS_ARM64_BINARY=..\..\bin\app.exe +## For a installer with both architectures: +## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app-amd64.exe -DARG_WAILS_ARM64_BINARY=..\..\bin\app-arm64.exe +#### +## The following information is taken from the wails_tools.nsh file, but they can be overwritten here. +#### +## !define INFO_PROJECTNAME "my-project" # Default "dock" +## !define INFO_COMPANYNAME "My Company" # Default "My Company" +## !define INFO_PRODUCTNAME "My Product Name" # Default "My Product" +## !define INFO_PRODUCTVERSION "1.0.0" # Default "0.1.0" +## !define INFO_COPYRIGHT "(c) Now, My Company" # Default "© now, My Company" +### +## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe" +## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" +#### +## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html +#### +## Include the wails tools +#### +!include "wails_tools.nsh" + +# The version information for this two must consist of 4 parts +VIProductVersion "${INFO_PRODUCTVERSION}.0" +VIFileVersion "${INFO_PRODUCTVERSION}.0" + +VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}" +VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer" +VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}" +VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}" +VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}" +VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}" + +# Enable HiDPI support. https://nsis.sourceforge.io/Reference/ManifestDPIAware +ManifestDPIAware true + +!include "MUI.nsh" + +!define MUI_ICON "..\icon.ico" +!define MUI_UNICON "..\icon.ico" +# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314 +!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps +!define MUI_ABORTWARNING # This will warn the user if they exit from the installer. + +!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page. +# !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer +!insertmacro MUI_PAGE_DIRECTORY # In which folder install page. +!insertmacro MUI_PAGE_INSTFILES # Installing page. +!insertmacro MUI_PAGE_FINISH # Finished installation page. + +!insertmacro MUI_UNPAGE_INSTFILES # Uninstalling page + +!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer + +## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1 +#!uninstfinalize 'signtool --file "%1"' +#!finalize 'signtool --file "%1"' + +Name "${INFO_PRODUCTNAME}" +OutFile "..\..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file. +InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder). +ShowInstDetails show # This will always show the installation details. + +Function .onInit + !insertmacro wails.checkArchitecture +FunctionEnd + +Section + !insertmacro wails.setShellContext + + !insertmacro wails.webview2runtime + + SetOutPath $INSTDIR + + !insertmacro wails.files + + CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" + CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" + + !insertmacro wails.associateFiles + + !insertmacro wails.writeUninstaller +SectionEnd + +Section "uninstall" + !insertmacro wails.setShellContext + + RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath + + RMDir /r $INSTDIR + + Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" + Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk" + + !insertmacro wails.unassociateFiles + + !insertmacro wails.deleteUninstaller +SectionEnd diff --git a/v3/examples/dock/build/windows/nsis/wails_tools.nsh b/v3/examples/dock/build/windows/nsis/wails_tools.nsh new file mode 100644 index 000000000..b5ff5b23c --- /dev/null +++ b/v3/examples/dock/build/windows/nsis/wails_tools.nsh @@ -0,0 +1,212 @@ +# DO NOT EDIT - Generated automatically by `wails build` + +!include "x64.nsh" +!include "WinVer.nsh" +!include "FileFunc.nsh" + +!ifndef INFO_PROJECTNAME + !define INFO_PROJECTNAME "dock" +!endif +!ifndef INFO_COMPANYNAME + !define INFO_COMPANYNAME "My Company" +!endif +!ifndef INFO_PRODUCTNAME + !define INFO_PRODUCTNAME "My Product" +!endif +!ifndef INFO_PRODUCTVERSION + !define INFO_PRODUCTVERSION "0.1.0" +!endif +!ifndef INFO_COPYRIGHT + !define INFO_COPYRIGHT "© now, My Company" +!endif +!ifndef PRODUCT_EXECUTABLE + !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe" +!endif +!ifndef UNINST_KEY_NAME + !define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" +!endif +!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}" + +!ifndef REQUEST_EXECUTION_LEVEL + !define REQUEST_EXECUTION_LEVEL "admin" +!endif + +RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}" + +!ifdef ARG_WAILS_AMD64_BINARY + !define SUPPORTS_AMD64 +!endif + +!ifdef ARG_WAILS_ARM64_BINARY + !define SUPPORTS_ARM64 +!endif + +!ifdef SUPPORTS_AMD64 + !ifdef SUPPORTS_ARM64 + !define ARCH "amd64_arm64" + !else + !define ARCH "amd64" + !endif +!else + !ifdef SUPPORTS_ARM64 + !define ARCH "arm64" + !else + !error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY" + !endif +!endif + +!macro wails.checkArchitecture + !ifndef WAILS_WIN10_REQUIRED + !define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later." + !endif + + !ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED + !define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}" + !endif + + ${If} ${AtLeastWin10} + !ifdef SUPPORTS_AMD64 + ${if} ${IsNativeAMD64} + Goto ok + ${EndIf} + !endif + + !ifdef SUPPORTS_ARM64 + ${if} ${IsNativeARM64} + Goto ok + ${EndIf} + !endif + + IfSilent silentArch notSilentArch + silentArch: + SetErrorLevel 65 + Abort + notSilentArch: + MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}" + Quit + ${else} + IfSilent silentWin notSilentWin + silentWin: + SetErrorLevel 64 + Abort + notSilentWin: + MessageBox MB_OK "${WAILS_WIN10_REQUIRED}" + Quit + ${EndIf} + + ok: +!macroend + +!macro wails.files + !ifdef SUPPORTS_AMD64 + ${if} ${IsNativeAMD64} + File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}" + ${EndIf} + !endif + + !ifdef SUPPORTS_ARM64 + ${if} ${IsNativeARM64} + File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}" + ${EndIf} + !endif +!macroend + +!macro wails.writeUninstaller + WriteUninstaller "$INSTDIR\uninstall.exe" + + SetRegView 64 + WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}" + WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" + + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0" +!macroend + +!macro wails.deleteUninstaller + Delete "$INSTDIR\uninstall.exe" + + SetRegView 64 + DeleteRegKey HKLM "${UNINST_KEY}" +!macroend + +!macro wails.setShellContext + ${If} ${REQUEST_EXECUTION_LEVEL} == "admin" + SetShellVarContext all + ${else} + SetShellVarContext current + ${EndIf} +!macroend + +# Install webview2 by launching the bootstrapper +# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment +!macro wails.webview2runtime + !ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT + !define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime" + !endif + + SetRegView 64 + # If the admin key exists and is not empty then webview2 is already installed + ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ${If} $0 != "" + Goto ok + ${EndIf} + + ${If} ${REQUEST_EXECUTION_LEVEL} == "user" + # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed + ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ${If} $0 != "" + Goto ok + ${EndIf} + ${EndIf} + + SetDetailsPrint both + DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}" + SetDetailsPrint listonly + + InitPluginsDir + CreateDirectory "$pluginsdir\webview2bootstrapper" + SetOutPath "$pluginsdir\webview2bootstrapper" + File "MicrosoftEdgeWebview2Setup.exe" + ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install' + + SetDetailsPrint both + ok: +!macroend + +# Copy of APP_ASSOCIATE and APP_UNASSOCIATE macros from here https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b +!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND + ; Backup the previously associated file class + ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0" + + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}" + + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open" + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}` +!macroend + +!macro APP_UNASSOCIATE EXT FILECLASS + ; Backup the previously associated file class + ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup` + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0" + + DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}` +!macroend + +!macro wails.associateFiles + ; Create file associations + +!macroend + +!macro wails.unassociateFiles + ; Delete app associations + +!macroend \ No newline at end of file diff --git a/v3/examples/dock/build/windows/wails.exe.manifest b/v3/examples/dock/build/windows/wails.exe.manifest new file mode 100644 index 000000000..48052e7e6 --- /dev/null +++ b/v3/examples/dock/build/windows/wails.exe.manifest @@ -0,0 +1,22 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + + + + + + + \ No newline at end of file diff --git a/v3/examples/dock/frontend/Inter Font License.txt b/v3/examples/dock/frontend/Inter Font License.txt new file mode 100644 index 000000000..b525cbf3a --- /dev/null +++ b/v3/examples/dock/frontend/Inter Font License.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts new file mode 100644 index 000000000..abf1f22e4 --- /dev/null +++ b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/dockservice.ts @@ -0,0 +1,53 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * Service represents the dock service + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * HideAppIcon hides the app icon in the dock/taskbar. + */ +export function HideAppIcon(): $CancellablePromise { + return $Call.ByID(3413658144); +} + +/** + * RemoveBadge removes the badge label from the application icon. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function RemoveBadge(): $CancellablePromise { + return $Call.ByID(2752757297); +} + +/** + * SetBadge sets the badge label on the application icon. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function SetBadge(label: string): $CancellablePromise { + return $Call.ByID(1717705661, label); +} + +/** + * SetCustomBadge sets the badge label on the application icon with custom options. + * This method ensures the badge call is made on the main thread to avoid crashes. + */ +export function SetCustomBadge(label: string, options: $models.BadgeOptions): $CancellablePromise { + return $Call.ByID(2730169760, label, options); +} + +/** + * ShowAppIcon shows the app icon in the dock/taskbar. + */ +export function ShowAppIcon(): $CancellablePromise { + return $Call.ByID(3409697379); +} diff --git a/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts new file mode 100644 index 000000000..fbdaf19f3 --- /dev/null +++ b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts @@ -0,0 +1,11 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as DockService from "./dockservice.js"; +export { + DockService +}; + +export { + BadgeOptions +} from "./models.js"; diff --git a/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts new file mode 100644 index 000000000..f97c8a4c6 --- /dev/null +++ b/v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts @@ -0,0 +1,61 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Create as $Create } from "@wailsio/runtime"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as color$0 from "../../../../../../../image/color/models.js"; + +/** + * BadgeOptions represents options for customizing badge appearance + */ +export class BadgeOptions { + "TextColour": color$0.RGBA; + "BackgroundColour": color$0.RGBA; + "FontName": string; + "FontSize": number; + "SmallFontSize": number; + + /** Creates a new BadgeOptions instance. */ + constructor($$source: Partial = {}) { + if (!("TextColour" in $$source)) { + this["TextColour"] = (new color$0.RGBA()); + } + if (!("BackgroundColour" in $$source)) { + this["BackgroundColour"] = (new color$0.RGBA()); + } + if (!("FontName" in $$source)) { + this["FontName"] = ""; + } + if (!("FontSize" in $$source)) { + this["FontSize"] = 0; + } + if (!("SmallFontSize" in $$source)) { + this["SmallFontSize"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new BadgeOptions instance from a string or object. + */ + static createFrom($$source: any = {}): BadgeOptions { + const $$createField0_0 = $$createType0; + const $$createField1_0 = $$createType0; + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + if ("TextColour" in $$parsedSource) { + $$parsedSource["TextColour"] = $$createField0_0($$parsedSource["TextColour"]); + } + if ("BackgroundColour" in $$parsedSource) { + $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]); + } + return new BadgeOptions($$parsedSource as Partial); + } +} + +// Private type creation functions +const $$createType0 = color$0.RGBA.createFrom; diff --git a/v3/examples/dock/frontend/bindings/image/color/index.ts b/v3/examples/dock/frontend/bindings/image/color/index.ts new file mode 100644 index 000000000..97b507b08 --- /dev/null +++ b/v3/examples/dock/frontend/bindings/image/color/index.ts @@ -0,0 +1,6 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export { + RGBA +} from "./models.js"; diff --git a/v3/examples/dock/frontend/bindings/image/color/models.ts b/v3/examples/dock/frontend/bindings/image/color/models.ts new file mode 100644 index 000000000..0d4eab56d --- /dev/null +++ b/v3/examples/dock/frontend/bindings/image/color/models.ts @@ -0,0 +1,46 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Create as $Create } from "@wailsio/runtime"; + +/** + * RGBA represents a traditional 32-bit alpha-premultiplied color, having 8 + * bits for each of red, green, blue and alpha. + * + * An alpha-premultiplied color component C has been scaled by alpha (A), so + * has valid values 0 <= C <= A. + */ +export class RGBA { + "R": number; + "G": number; + "B": number; + "A": number; + + /** Creates a new RGBA instance. */ + constructor($$source: Partial = {}) { + if (!("R" in $$source)) { + this["R"] = 0; + } + if (!("G" in $$source)) { + this["G"] = 0; + } + if (!("B" in $$source)) { + this["B"] = 0; + } + if (!("A" in $$source)) { + this["A"] = 0; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new RGBA instance from a string or object. + */ + static createFrom($$source: any = {}): RGBA { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new RGBA($$parsedSource as Partial); + } +} diff --git a/v3/examples/dock/frontend/dist/Inter-Medium.ttf b/v3/examples/dock/frontend/dist/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/dock/frontend/dist/Inter-Medium.ttf differ diff --git a/v3/examples/dock/frontend/dist/assets/index-BJIh-HIc.js b/v3/examples/dock/frontend/dist/assets/index-BJIh-HIc.js new file mode 100644 index 000000000..5217ab9a7 --- /dev/null +++ b/v3/examples/dock/frontend/dist/assets/index-BJIh-HIc.js @@ -0,0 +1,6 @@ +(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))n(o);new MutationObserver(o=>{for(const i of o)if(i.type==="childList")for(const s of i.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&n(s)}).observe(document,{childList:!0,subtree:!0});function r(o){const i={};return o.integrity&&(i.integrity=o.integrity),o.referrerPolicy&&(i.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?i.credentials="include":o.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(o){if(o.ep)return;o.ep=!0;const i=r(o);fetch(o.href,i)}})();const ie="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";function K(t=21){let e="",r=t|0;for(;r--;)e+=ie[Math.random()*64|0];return e}const se=window.location.origin+"/wails/runtime",O=Object.freeze({Call:0,Clipboard:1,Application:2,Events:3,ContextMenu:4,Dialog:5,Window:6,Screens:7,System:8,Browser:9,CancelCall:10});let ce=K();function R(t,e=""){return function(r,n=null){return le(t,r,e,n)}}async function le(t,e,r,n){var o,i;let s=new URL(se);s.searchParams.append("object",t.toString()),s.searchParams.append("method",e.toString()),n&&s.searchParams.append("args",JSON.stringify(n));let l={"x-wails-client-id":ce};r&&(l["x-wails-window-name"]=r);let c=await fetch(s,{headers:l});if(!c.ok)throw new Error(await c.text());return((i=(o=c.headers.get("Content-Type"))===null||o===void 0?void 0:o.indexOf("application/json"))!==null&&i!==void 0?i:-1)!==-1?c.json():c.text()}R(O.System);const P=function(){var t,e,r,n,o;try{if(!((e=(t=window.chrome)===null||t===void 0?void 0:t.webview)===null||e===void 0)&&e.postMessage)return window.chrome.webview.postMessage.bind(window.chrome.webview);if(!((o=(n=(r=window.webkit)===null||r===void 0?void 0:r.messageHandlers)===null||n===void 0?void 0:n.external)===null||o===void 0)&&o.postMessage)return window.webkit.messageHandlers.external.postMessage.bind(window.webkit.messageHandlers.external)}catch{}return console.warn(` +%c⚠️ Browser Environment Detected %c + +%cOnly UI previews are available in the browser. For full functionality, please run the application in desktop mode. +More information at: https://v3.wails.io/learn/build/#using-a-browser-for-development +`,"background: #ffffff; color: #000000; font-weight: bold; padding: 4px 8px; border-radius: 4px; border: 2px solid #000000;","background: transparent;","color: #ffffff; font-style: italic; font-weight: bold;"),null}();function M(t){P==null||P(t)}function Q(){return window._wails.environment.OS==="windows"}function ae(){return!!window._wails.environment.Debug}function ue(){return new MouseEvent("mousedown").buttons===0}function Z(t){var e;return t.target instanceof HTMLElement?t.target:!(t.target instanceof HTMLElement)&&t.target instanceof Node&&(e=t.target.parentElement)!==null&&e!==void 0?e:document.body}document.addEventListener("DOMContentLoaded",()=>{});window.addEventListener("contextmenu",pe);const de=R(O.ContextMenu),fe=0;function we(t,e,r,n){de(fe,{id:t,x:e,y:r,data:n})}function pe(t){const e=Z(t),r=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu").trim();if(r){t.preventDefault();const n=window.getComputedStyle(e).getPropertyValue("--custom-contextmenu-data");we(r,t.clientX,t.clientY,n)}else he(t,e)}function he(t,e){if(ae())return;switch(window.getComputedStyle(e).getPropertyValue("--default-contextmenu").trim()){case"show":return;case"hide":t.preventDefault();return}if(e.isContentEditable)return;const r=window.getSelection(),n=r&&r.toString().length>0;if(n)for(let o=0;o{F=t,F||(b=v=!1,u())};window.addEventListener("mousedown",N,{capture:!0});window.addEventListener("mousemove",N,{capture:!0});window.addEventListener("mouseup",N,{capture:!0});for(const t of["click","contextmenu","dblclick"])window.addEventListener(t,me,{capture:!0});function me(t){(E||v)&&(t.stopImmediatePropagation(),t.stopPropagation(),t.preventDefault())}const D=0,ye=1,k=2;function N(t){let e,r=t.buttons;switch(t.type){case"mousedown":e=D,H||(r=h|1<"u"||typeof e=="object"))try{var r=C.call(e);return(r===Ce||r===Me||r===Oe||r===Se)&&e("")==null}catch{}return!1})}function Pe(t){if(X(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;try{y(t,null,B)}catch(e){if(e!==x)return!1}return!W(t)&&_(t)}function Ae(t){if(X(t))return!0;if(!t||typeof t!="function"&&typeof t!="object")return!1;if(Re)return _(t);if(W(t))return!1;var e=C.call(t);return e!==ze&&e!==xe&&!/^\[object HTML/.test(e)?!1:_(t)}const p=y?Pe:Ae;var I;class U extends Error{constructor(e,r){super(e,r),this.name="CancelError"}}class S extends Error{constructor(e,r,n){super((n??"Unhandled rejection in cancelled promise.")+" Reason: "+He(r),{cause:r}),this.promise=e,this.name="CancelledRejectionError"}}const f=Symbol("barrier"),J=Symbol("cancelImpl"),V=(I=Symbol.species)!==null&&I!==void 0?I:Symbol("speciesPolyfill");class a extends Promise{constructor(e,r){let n,o;if(super((c,d)=>{n=c,o=d}),this.constructor[V]!==Promise)throw new TypeError("CancellablePromise does not support transparent subclassing. Please refrain from overriding the [Symbol.species] static property.");let i={promise:this,resolve:n,reject:o,get oncancelled(){return r??null},set oncancelled(c){r=c??void 0}};const s={get root(){return s},resolving:!1,settled:!1};Object.defineProperties(this,{[f]:{configurable:!1,enumerable:!1,writable:!0,value:null},[J]:{configurable:!1,enumerable:!1,writable:!1,value:ee(i,s)}});const l=re(i,s);try{e(te(i,s),l)}catch(c){s.resolving?console.log("Unhandled exception in CancellablePromise executor.",c):l(c)}}cancel(e){return new a(r=>{Promise.all([this[J](new U("Promise cancelled.",{cause:e})),De(this)]).then(()=>r(),()=>r())})}cancelOn(e){return e.aborted?this.cancel(e.reason):e.addEventListener("abort",()=>void this.cancel(e.reason),{capture:!0}),this}then(e,r,n){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.then called on an invalid object.");if(p(e)||(e=q),p(r)||(r=G),e===q&&r==G)return new a(i=>i(this));const o={};return this[f]=o,new a((i,s)=>{super.then(l=>{var c;this[f]===o&&(this[f]=null),(c=o.resolve)===null||c===void 0||c.call(o);try{i(e(l))}catch(d){s(d)}},l=>{var c;this[f]===o&&(this[f]=null),(c=o.resolve)===null||c===void 0||c.call(o);try{i(r(l))}catch(d){s(d)}})},async i=>{try{return n==null?void 0:n(i)}finally{await this.cancel(i)}})}catch(e,r){return this.then(void 0,e,r)}finally(e,r){if(!(this instanceof a))throw new TypeError("CancellablePromise.prototype.finally called on an invalid object.");return p(e)?this.then(n=>a.resolve(e()).then(()=>n),n=>a.resolve(e()).then(()=>{throw n}),r):this.then(e,e,r)}static get[V](){return Promise}static all(e){let r=Array.from(e);const n=r.length===0?a.resolve(r):new a((o,i)=>{Promise.all(r).then(o,i)},o=>z(n,r,o));return n}static allSettled(e){let r=Array.from(e);const n=r.length===0?a.resolve(r):new a((o,i)=>{Promise.allSettled(r).then(o,i)},o=>z(n,r,o));return n}static any(e){let r=Array.from(e);const n=r.length===0?a.resolve(r):new a((o,i)=>{Promise.any(r).then(o,i)},o=>z(n,r,o));return n}static race(e){let r=Array.from(e);const n=new a((o,i)=>{Promise.race(r).then(o,i)},o=>z(n,r,o));return n}static cancel(e){const r=new a(()=>{});return r.cancel(e),r}static timeout(e,r){const n=new a(()=>{});return AbortSignal&&typeof AbortSignal=="function"&&AbortSignal.timeout&&typeof AbortSignal.timeout=="function"?AbortSignal.timeout(e).addEventListener("abort",()=>void n.cancel(r)):setTimeout(()=>void n.cancel(r),e),n}static sleep(e,r){return new a(n=>{setTimeout(()=>n(r),e)})}static reject(e){return new a((r,n)=>n(e))}static resolve(e){return e instanceof a?e:new a(r=>r(e))}static withResolvers(){let e={oncancelled:null};return e.promise=new a((r,n)=>{e.resolve=r,e.reject=n},r=>{var n;(n=e.oncancelled)===null||n===void 0||n.call(e,r)}),e}}function ee(t,e){let r;return n=>{if(e.settled||(e.settled=!0,e.reason=n,t.reject(n),Promise.prototype.then.call(t.promise,void 0,o=>{if(o!==n)throw o})),!(!e.reason||!t.oncancelled))return r=new Promise(o=>{try{o(t.oncancelled(e.reason.cause))}catch(i){Promise.reject(new S(t.promise,i,"Unhandled exception in oncancelled callback."))}}).catch(o=>{Promise.reject(new S(t.promise,o,"Unhandled rejection in oncancelled callback."))}),t.oncancelled=null,r}}function te(t,e){return r=>{if(!e.resolving){if(e.resolving=!0,r===t.promise){if(e.settled)return;e.settled=!0,t.reject(new TypeError("A promise cannot be resolved with itself."));return}if(r!=null&&(typeof r=="object"||typeof r=="function")){let n;try{n=r.then}catch(o){e.settled=!0,t.reject(o);return}if(p(n)){try{let s=r.cancel;if(p(s)){const l=c=>{Reflect.apply(s,r,[c])};e.reason?ee(Object.assign(Object.assign({},t),{oncancelled:l}),e)(e.reason):t.oncancelled=l}}catch{}const o={root:e.root,resolving:!1,get settled(){return this.root.settled},set settled(s){this.root.settled=s},get reason(){return this.root.reason}},i=re(t,o);try{Reflect.apply(n,r,[te(t,o),i])}catch(s){i(s)}return}}e.settled||(e.settled=!0,t.resolve(r))}}}function re(t,e){return r=>{if(!e.resolving)if(e.resolving=!0,e.settled){try{if(r instanceof U&&e.reason instanceof U&&Object.is(r.cause,e.reason.cause))return}catch{}Promise.reject(new S(t.promise,r))}else e.settled=!0,t.reject(r)}}function z(t,e,r){const n=[];for(const o of e){let i;try{if(!p(o.then)||(i=o.cancel,!p(i)))continue}catch{continue}let s;try{s=Reflect.apply(i,o,[r])}catch(l){Promise.reject(new S(t,l,"Unhandled exception in cancel method."));continue}s&&n.push((s instanceof Promise?s:Promise.resolve(s)).catch(l=>{Promise.reject(new S(t,l,"Unhandled rejection in cancel method."))}))}return Promise.all(n)}function q(t){return t}function G(t){throw t}function He(t){try{if(t instanceof Error||typeof t!="object"||t.toString!==Object.prototype.toString)return""+t}catch{}try{return JSON.stringify(t)}catch{}try{return Object.prototype.toString.call(t)}catch{}return""}function De(t){var e;let r=(e=t[f])!==null&&e!==void 0?e:{};return"promise"in r||Object.assign(r,m()),t[f]==null&&(r.resolve(),t[f]=r),r.promise}let m=Promise.withResolvers;m&&typeof m=="function"?m=m.bind(Promise):m=function(){let t,e;return{promise:new Promise((n,o)=>{t=n,e=o}),resolve:t,reject:e}};window._wails=window._wails||{};window._wails.callResultHandler=Ue;window._wails.callErrorHandler=Ne;const ke=R(O.Call),Ie=R(O.CancelCall),g=new Map,Fe=0,Be=0;class _e extends Error{constructor(e,r){super(e,r),this.name="RuntimeError"}}function Ue(t,e,r){const n=ne(t);if(n)if(!e)n.resolve(void 0);else if(!r)n.resolve(e);else try{n.resolve(JSON.parse(e))}catch(o){n.reject(new TypeError("could not parse result: "+o.message,{cause:o}))}}function Ne(t,e,r){const n=ne(t);if(n)if(!r)n.reject(new Error(e));else{let o;try{o=JSON.parse(e)}catch(l){n.reject(new TypeError("could not parse error: "+l.message,{cause:l}));return}let i={};o.cause&&(i.cause=o.cause);let s;switch(o.kind){case"ReferenceError":s=new ReferenceError(o.message,i);break;case"TypeError":s=new TypeError(o.message,i);break;case"RuntimeError":s=new _e(o.message,i);break;default:s=new Error(o.message,i);break}n.reject(s)}}function ne(t){const e=g.get(t);return g.delete(t),e}function We(){let t;do t=K();while(g.has(t));return t}function Xe(t){const e=We(),r=a.withResolvers();g.set(e,{resolve:r.resolve,reject:r.reject});const n=ke(Fe,Object.assign({"call-id":e},t));let o=!1;n.then(()=>{o=!0},s=>{g.delete(e),r.reject(s)});const i=()=>(g.delete(e),Ie(Be,{"call-id":e}).catch(s=>{console.error("Error while requesting binding call cancellation:",s)}));return r.oncancelled=()=>o?i():n.then(i),r.promise}function oe(t,...e){return Xe({methodID:t,args:e})}window._wails=window._wails||{};window._wails.invoke=M;M("wails:runtime:ready");function Ye(){return oe(3413658144)}function Je(){return oe(3409697379)}const Ve=document.getElementById("show"),qe=document.getElementById("hide");Ve.addEventListener("click",()=>{Je()});qe.addEventListener("click",()=>{Ye()}); diff --git a/v3/examples/dock/frontend/dist/index.html b/v3/examples/dock/frontend/dist/index.html new file mode 100644 index 000000000..1836ad6e7 --- /dev/null +++ b/v3/examples/dock/frontend/dist/index.html @@ -0,0 +1,33 @@ + + + + + + + + Wails App + + + +
+ +

Wails + Typescript

+
+
+ + +
+
+ +
+ + diff --git a/v3/examples/dock/frontend/dist/style.css b/v3/examples/dock/frontend/dist/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/examples/dock/frontend/dist/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/dock/frontend/dist/typescript.svg b/v3/examples/dock/frontend/dist/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/dock/frontend/dist/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/dock/frontend/dist/wails.png b/v3/examples/dock/frontend/dist/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/dock/frontend/dist/wails.png differ diff --git a/v3/examples/dock/frontend/index.html b/v3/examples/dock/frontend/index.html new file mode 100644 index 000000000..1a310f0e9 --- /dev/null +++ b/v3/examples/dock/frontend/index.html @@ -0,0 +1,33 @@ + + + + + + + + Wails App + + +
+ +

Wails + Typescript

+
+
+ + +
+
+ +
+ + + diff --git a/v3/examples/dock/frontend/package-lock.json b/v3/examples/dock/frontend/package-lock.json new file mode 100644 index 000000000..0c0df727a --- /dev/null +++ b/v3/examples/dock/frontend/package-lock.json @@ -0,0 +1,936 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^4.9.3", + "vite": "^5.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wailsio/runtime": { + "version": "3.0.0-alpha.66", + "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.66.tgz", + "integrity": "sha512-ENLu8rn1griL1gFHJqkq1i+BVxrrA0JPJHYneUJYuf/s54kjuQViW0RKDEe/WTDo56ABpfykrd/T8OYpPUyXUw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/v3/examples/dock/frontend/package.json b/v3/examples/dock/frontend/package.json new file mode 100644 index 000000000..b39da7ece --- /dev/null +++ b/v3/examples/dock/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build:dev": "tsc && vite build --minify false --mode development", + "build": "tsc && vite build --mode production", + "preview": "vite preview" + }, + "dependencies": { + "@wailsio/runtime": "latest" + }, + "devDependencies": { + "typescript": "^4.9.3", + "vite": "^5.0.0" + } +} diff --git a/v3/examples/dock/frontend/public/Inter-Medium.ttf b/v3/examples/dock/frontend/public/Inter-Medium.ttf new file mode 100644 index 000000000..a01f3777a Binary files /dev/null and b/v3/examples/dock/frontend/public/Inter-Medium.ttf differ diff --git a/v3/examples/dock/frontend/public/style.css b/v3/examples/dock/frontend/public/style.css new file mode 100644 index 000000000..0b9c58279 --- /dev/null +++ b/v3/examples/dock/frontend/public/style.css @@ -0,0 +1,157 @@ +:root { + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: rgba(27, 38, 54, 1); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 400; + src: local(""), + url("./Inter-Medium.ttf") format("truetype"); +} + +h3 { + font-size: 3em; + line-height: 1.1; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} + +a:hover { + color: #535bf2; +} + +button { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} + +.result { + height: 20px; + line-height: 20px; +} + +body { + margin: 0; + display: flex; + place-items: center; + place-content: center; + min-width: 320px; + min-height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #e80000aa); +} + +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #f7df1eaa); +} + +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; + text-align: center; +} + +.footer { + margin-top: 1rem; + align-content: center; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + + a:hover { + color: #747bff; + } + + button { + background-color: #f9f9f9; + } +} + + +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + color: black; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} \ No newline at end of file diff --git a/v3/examples/dock/frontend/public/typescript.svg b/v3/examples/dock/frontend/public/typescript.svg new file mode 100644 index 000000000..d91c910cc --- /dev/null +++ b/v3/examples/dock/frontend/public/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v3/examples/dock/frontend/public/wails.png b/v3/examples/dock/frontend/public/wails.png new file mode 100644 index 000000000..8bdf42483 Binary files /dev/null and b/v3/examples/dock/frontend/public/wails.png differ diff --git a/v3/examples/dock/frontend/src/main.ts b/v3/examples/dock/frontend/src/main.ts new file mode 100644 index 000000000..6ef5af25f --- /dev/null +++ b/v3/examples/dock/frontend/src/main.ts @@ -0,0 +1,12 @@ +import { DockService } from "../bindings/github.com/wailsapp/wails/v3/pkg/services/dock" + +const showButton = document.getElementById('show')! as HTMLButtonElement; +const hideButton = document.getElementById('hide')! as HTMLButtonElement; + +showButton.addEventListener('click', () => { + DockService.ShowAppIcon(); +}); + +hideButton.addEventListener('click', () => { + DockService.HideAppIcon(); +}); \ No newline at end of file diff --git a/v3/examples/dock/frontend/src/vite-env.d.ts b/v3/examples/dock/frontend/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/v3/examples/dock/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/v3/examples/dock/frontend/tsconfig.json b/v3/examples/dock/frontend/tsconfig.json new file mode 100644 index 000000000..c267ecf24 --- /dev/null +++ b/v3/examples/dock/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": false, + "noImplicitAny": false, + "noImplicitReturns": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/v3/examples/dock/main.go b/v3/examples/dock/main.go new file mode 100644 index 000000000..446645448 --- /dev/null +++ b/v3/examples/dock/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "embed" + _ "embed" + "log" + + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/services/dock" +) + +// Wails uses Go's `embed` package to embed the frontend files into the binary. +// Any files in the frontend/dist folder will be embedded into the binary and +// made available to the frontend. +// See https://pkg.go.dev/embed for more information. + +//go:embed all:frontend/dist +var assets embed.FS + +// main function serves as the application's entry point. It initializes the application, creates a window, +// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and +// logs any error that might occur. +func main() { + + // Create a new Wails application by providing the necessary options. + // Variables 'Name' and 'Description' are for application metadata. + // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. + // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. + // 'Mac' options tailor the application when running an macOS. + + dockService := dock.New() + + app := application.New(application.Options{ + Name: "dock", + Description: "A demo of using raw HTML & CSS", + Services: []application.Service{ + application.NewService(dockService), + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create a new window with the necessary options. + // 'Title' is the title of the window. + // 'Mac' options tailor the window when running on macOS. + // 'BackgroundColour' is the background colour of the window. + // 'URL' is the URL that will be loaded into the webview. + app.Window.NewWithOptions(application.WebviewWindowOptions{ + Title: "Window 1", + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + Backdrop: application.MacBackdropTranslucent, + TitleBar: application.MacTitleBarHiddenInset, + }, + BackgroundColour: application.NewRGB(27, 38, 54), + URL: "/", + }) + + // Run the application. This blocks until the application has been exited. + err := app.Run() + + // If an error occurred while running the application, log it and exit. + if err != nil { + log.Fatal(err) + } +} diff --git a/v3/pkg/services/badge/badge.go b/v3/pkg/services/badge/badge.go deleted file mode 100644 index 29b363082..000000000 --- a/v3/pkg/services/badge/badge.go +++ /dev/null @@ -1,60 +0,0 @@ -package badge - -import ( - "context" - "image/color" - - "github.com/wailsapp/wails/v3/pkg/application" -) - -type platformBadge interface { - // Lifecycle methods - Startup(ctx context.Context, options application.ServiceOptions) error - Shutdown() error - - SetBadge(label string) error - SetCustomBadge(label string, options Options) error - RemoveBadge() error -} - -// Service represents the notifications service -type BadgeService struct { - impl platformBadge -} - -type Options struct { - TextColour color.RGBA - BackgroundColour color.RGBA - FontName string - FontSize int - SmallFontSize int -} - -// ServiceName returns the name of the service. -func (b *BadgeService) ServiceName() string { - return "github.com/wailsapp/wails/v3/services/badge" -} - -// ServiceStartup is called when the service is loaded. -func (b *BadgeService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { - return b.impl.Startup(ctx, options) -} - -// ServiceShutdown is called when the service is unloaded. -func (b *BadgeService) ServiceShutdown() error { - return b.impl.Shutdown() -} - -// SetBadge sets the badge label on the application icon. -func (b *BadgeService) SetBadge(label string) error { - return b.impl.SetBadge(label) -} - -func (b *BadgeService) SetCustomBadge(label string, options Options) error { - return b.impl.SetCustomBadge(label, options) -} - -// RemoveBadge removes the badge label from the application icon. -func (b *BadgeService) RemoveBadge() error { - return b.impl.RemoveBadge() -} diff --git a/v3/pkg/services/badge/badge_darwin.go b/v3/pkg/services/badge/badge_darwin.go deleted file mode 100644 index cf61c71ca..000000000 --- a/v3/pkg/services/badge/badge_darwin.go +++ /dev/null @@ -1,74 +0,0 @@ -//go:build darwin - -package badge - -/* -#cgo CFLAGS: -x objective-c -#cgo LDFLAGS: -framework Cocoa -#import - -static void setBadge(const char *label) { - NSString *nsLabel = nil; - if (label != NULL) { - nsLabel = [NSString stringWithUTF8String:label]; - } - [[NSApp dockTile] setBadgeLabel:nsLabel]; - [[NSApp dockTile] display]; -} -*/ -import "C" -import ( - "context" - "unsafe" - - "github.com/wailsapp/wails/v3/pkg/application" -) - -type darwinBadge struct{} - -// Creates a new Badge Service. -func New() *BadgeService { - return &BadgeService{ - impl: &darwinBadge{}, - } -} - -// NewWithOptions creates a new badge service with the given options. -// Currently, options are not available on macOS and are ignored. -// (Windows-specific) -func NewWithOptions(options Options) *BadgeService { - return New() -} - -func (d *darwinBadge) Startup(ctx context.Context, options application.ServiceOptions) error { - return nil -} - -func (d *darwinBadge) Shutdown() error { - return nil -} - -// SetBadge sets the badge label on the application icon. -func (d *darwinBadge) SetBadge(label string) error { - var cLabel *C.char - if label != "" { - cLabel = C.CString(label) - defer C.free(unsafe.Pointer(cLabel)) - } else { - cLabel = C.CString("●") // Default badge character - } - C.setBadge(cLabel) - return nil -} - -// SetCustomBadge is not supported on macOS, SetBadge is called instead. -// (Windows-specific) -func (d *darwinBadge) SetCustomBadge(label string, options Options) error { - return d.SetBadge(label) -} - -// RemoveBadge removes the badge label from the application icon. -func (d *darwinBadge) RemoveBadge() error { - C.setBadge(nil) - return nil -} diff --git a/v3/pkg/services/badge/badge_linux.go b/v3/pkg/services/badge/badge_linux.go deleted file mode 100644 index aba02f47d..000000000 --- a/v3/pkg/services/badge/badge_linux.go +++ /dev/null @@ -1,58 +0,0 @@ -//go:build linux - -package badge - -import ( - "context" - - "github.com/wailsapp/wails/v3/pkg/application" -) - -type linuxBadge struct{} - -// New creates a new Badge Service. -// On Linux, this returns a no-op implementation since most desktop environments -// don't have standardized dock badge functionality. -func New() *BadgeService { - return &BadgeService{ - impl: &linuxBadge{}, - } -} - -// NewWithOptions creates a new badge service with the given options. -// On Linux, this returns a no-op implementation since most desktop environments -// don't have standardized dock badge functionality. Options are ignored. -func NewWithOptions(options Options) *BadgeService { - return New() -} - -func (l *linuxBadge) Startup(ctx context.Context, options application.ServiceOptions) error { - // No-op: Linux doesn't have standardized badge support - return nil -} - -func (l *linuxBadge) Shutdown() error { - // No-op: Linux doesn't have standardized badge support - return nil -} - -// SetBadge is a no-op on Linux since most desktop environments don't support -// application dock badges. This method exists for cross-platform compatibility. -func (l *linuxBadge) SetBadge(label string) error { - // No-op: Linux doesn't have standardized badge support - return nil -} - -// SetCustomBadge is a no-op on Linux since most desktop environments don't support -// application dock badges. This method exists for cross-platform compatibility. -func (l *linuxBadge) SetCustomBadge(label string, options Options) error { - // No-op: Linux doesn't have standardized badge support - return nil -} - -// RemoveBadge is a no-op on Linux since most desktop environments don't support -// application dock badges. This method exists for cross-platform compatibility. -func (l *linuxBadge) RemoveBadge() error { - // No-op: Linux doesn't have standardized badge support - return nil -} diff --git a/v3/pkg/services/dock/dock.go b/v3/pkg/services/dock/dock.go new file mode 100644 index 000000000..eb11f59ca --- /dev/null +++ b/v3/pkg/services/dock/dock.go @@ -0,0 +1,77 @@ +package dock + +import ( + "context" + "image/color" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type platformDock interface { + // Lifecycle methods + Startup(ctx context.Context, options application.ServiceOptions) error + Shutdown() error + + // Dock icon visibility methods + HideAppIcon() + ShowAppIcon() + + // Badge methods + SetBadge(label string) error + SetCustomBadge(label string, options BadgeOptions) error + RemoveBadge() error +} + +// Service represents the dock service +type DockService struct { + impl platformDock +} + +// BadgeOptions represents options for customizing badge appearance +type BadgeOptions struct { + TextColour color.RGBA + BackgroundColour color.RGBA + FontName string + FontSize int + SmallFontSize int +} + +// ServiceName returns the name of the service. +func (d *DockService) ServiceName() string { + return "github.com/wailsapp/wails/v3/pkg/services/dock" +} + +// ServiceStartup is called when the service is loaded. +func (d *DockService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { + return d.impl.Startup(ctx, options) +} + +// ServiceShutdown is called when the service is unloaded. +func (d *DockService) ServiceShutdown() error { + return d.impl.Shutdown() +} + +// HideAppIcon hides the app icon in the dock/taskbar. +func (d *DockService) HideAppIcon() { + d.impl.HideAppIcon() +} + +// ShowAppIcon shows the app icon in the dock/taskbar. +func (d *DockService) ShowAppIcon() { + d.impl.ShowAppIcon() +} + +// SetBadge sets the badge label on the application icon. +func (d *DockService) SetBadge(label string) error { + return d.impl.SetBadge(label) +} + +// SetCustomBadge sets the badge label on the application icon with custom options. +func (d *DockService) SetCustomBadge(label string, options BadgeOptions) error { + return d.impl.SetCustomBadge(label, options) +} + +// RemoveBadge removes the badge label from the application icon. +func (d *DockService) RemoveBadge() error { + return d.impl.RemoveBadge() +} diff --git a/v3/pkg/services/dock/dock_darwin.go b/v3/pkg/services/dock/dock_darwin.go new file mode 100644 index 000000000..c42ae6b0c --- /dev/null +++ b/v3/pkg/services/dock/dock_darwin.go @@ -0,0 +1,97 @@ +//go:build darwin + +package dock + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Cocoa +#import + +void hideDockIcon() { + dispatch_async(dispatch_get_main_queue(), ^{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; + }); +} + +void showDockIcon() { + dispatch_async(dispatch_get_main_queue(), ^{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + }); +} + +static void setBadge(const char *label) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString *nsLabel = nil; + if (label != NULL) { + nsLabel = [NSString stringWithUTF8String:label]; + } + [[NSApp dockTile] setBadgeLabel:nsLabel]; + [[NSApp dockTile] display]; + }); +} +*/ +import "C" +import ( + "context" + "unsafe" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type darwinDock struct{} + +// Creates a new Dock Service. +func New() *DockService { + return &DockService{ + impl: &darwinDock{}, + } +} + +// NewWithOptions creates a new dock service with badge options. +// Currently, options are not available on macOS and are ignored. +func NewWithOptions(options BadgeOptions) *DockService { + return New() +} + +func (d *darwinDock) Startup(ctx context.Context, options application.ServiceOptions) error { + return nil +} + +func (d *darwinDock) Shutdown() error { + return nil +} + +// HideAppIcon hides the app icon in the macOS Dock. +func (d *darwinDock) HideAppIcon() { + C.hideDockIcon() +} + +// ShowAppIcon shows the app icon in the macOS Dock. +func (d *darwinDock) ShowAppIcon() { + C.showDockIcon() +} + +// SetBadge sets the badge label on the application icon. +func (d *darwinDock) SetBadge(label string) error { + // Always pick a label (use “●” if empty), then allocate + free exactly once. + value := label + if value == "" { + value = "●" // Default badge character + } + cLabel := C.CString(value) + defer C.free(unsafe.Pointer(cLabel)) + + C.setBadge(cLabel) + return nil +} + +// SetCustomBadge is not supported on macOS, SetBadge is called instead. +func (d *darwinDock) SetCustomBadge(label string, options BadgeOptions) error { + return d.SetBadge(label) +} + +// RemoveBadge removes the badge label from the application icon. +func (d *darwinDock) RemoveBadge() error { + C.setBadge(nil) + return nil +} diff --git a/v3/pkg/services/dock/dock_linux.go b/v3/pkg/services/dock/dock_linux.go new file mode 100644 index 000000000..af0311c67 --- /dev/null +++ b/v3/pkg/services/dock/dock_linux.go @@ -0,0 +1,70 @@ +//go:build linux + +package dock + +import ( + "context" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type linuxDock struct{} + +// New creates a new Dock Service. +// On Linux, this returns a stub implementation since dock icon visibility +// and badge functionality are not standardized across desktop environments. +func New() *DockService { + return &DockService{ + impl: &linuxDock{}, + } +} + +// NewWithOptions creates a new dock service with badge options. +// On Linux, this returns a stub implementation since badge functionality +// is not standardized across desktop environments. Options are ignored. +func NewWithOptions(options BadgeOptions) *DockService { + return New() +} + +func (l *linuxDock) Startup(ctx context.Context, options application.ServiceOptions) error { + // No-op: Linux doesn't have standardized dock/badge support + return nil +} + +func (l *linuxDock) Shutdown() error { + // No-op: Linux doesn't have standardized dock/badge support + return nil +} + +// HideAppIcon is a stub on Linux since dock icon visibility is not +// standardized across desktop environments. +func (l *linuxDock) HideAppIcon() { + // No-op: Linux doesn't have standardized dock icon visibility support +} + +// ShowAppIcon is a stub on Linux since dock icon visibility is not +// standardized across desktop environments. +func (l *linuxDock) ShowAppIcon() { + // No-op: Linux doesn't have standardized dock icon visibility support +} + +// SetBadge is a stub on Linux since most desktop environments don't support +// application dock badges. This method exists for cross-platform compatibility. +func (l *linuxDock) SetBadge(label string) error { + // No-op: Linux doesn't have standardized badge support + return nil +} + +// SetCustomBadge is a stub on Linux since most desktop environments don't support +// application dock badges. This method exists for cross-platform compatibility. +func (l *linuxDock) SetCustomBadge(label string, options BadgeOptions) error { + // No-op: Linux doesn't have standardized badge support + return nil +} + +// RemoveBadge is a stub on Linux since most desktop environments don't support +// application dock badges. This method exists for cross-platform compatibility. +func (l *linuxDock) RemoveBadge() error { + // No-op: Linux doesn't have standardized badge support + return nil +} diff --git a/v3/pkg/services/badge/badge_windows.go b/v3/pkg/services/dock/dock_windows.go similarity index 53% rename from v3/pkg/services/badge/badge_windows.go rename to v3/pkg/services/dock/dock_windows.go index 3ef503e26..26f40d259 100644 --- a/v3/pkg/services/badge/badge_windows.go +++ b/v3/pkg/services/dock/dock_windows.go @@ -1,6 +1,6 @@ //go:build windows -package badge +package dock import ( "bytes" @@ -18,15 +18,15 @@ import ( "golang.org/x/image/math/fixed" ) -type windowsBadge struct { - taskbar *w32.ITaskbarList3 - badgeImg *image.RGBA - badgeSize int - fontManager *FontManager - options Options +type windowsDock struct { + taskbar *w32.ITaskbarList3 + badgeImg *image.RGBA + badgeSize int + fontManager *FontManager + badgeOptions BadgeOptions } -var defaultOptions = Options{ +var defaultOptions = BadgeOptions{ TextColour: color.RGBA{255, 255, 255, 255}, BackgroundColour: color.RGBA{255, 0, 0, 255}, FontName: "segoeuib.ttf", @@ -35,24 +35,24 @@ var defaultOptions = Options{ } // Creates a new Badge Service. -func New() *BadgeService { - return &BadgeService{ - impl: &windowsBadge{ - options: defaultOptions, +func New() *DockService { + return &DockService{ + impl: &windowsDock{ + badgeOptions: defaultOptions, }, } } -// NewWithOptions creates a new badge service with the given options. -func NewWithOptions(options Options) *BadgeService { - return &BadgeService{ - impl: &windowsBadge{ - options: options, +// NewWithOptions creates a new badge service with the given badge options. +func NewWithOptions(options BadgeOptions) *DockService { + return &DockService{ + impl: &windowsDock{ + badgeOptions: options, }, } } -func (w *windowsBadge) Startup(ctx context.Context, options application.ServiceOptions) error { +func (w *windowsDock) Startup(ctx context.Context, options application.ServiceOptions) error { taskbar, err := w32.NewTaskbarList3() if err != nil { return err @@ -63,7 +63,7 @@ func (w *windowsBadge) Startup(ctx context.Context, options application.ServiceO return nil } -func (w *windowsBadge) Shutdown() error { +func (w *windowsDock) Shutdown() error { if w.taskbar != nil { w.taskbar.Release() } @@ -71,134 +71,150 @@ func (w *windowsBadge) Shutdown() error { return nil } +// HideAppIcon hides the app icon in the macOS Dock. +func (w *windowsDock) HideAppIcon() { + // No-op: researching Windows options +} + +// ShowAppIcon shows the app icon in the macOS Dock. +func (w *windowsDock) ShowAppIcon() { + // No-op: researching Windows options +} + // SetBadge sets the badge label on the application icon. -func (w *windowsBadge) SetBadge(label string) error { - if w.taskbar == nil { - return nil - } - - app := application.Get() - if app == nil { - return nil - } - - window := app.Window.Current() - if window == nil { - return nil - } - - nativeWindow := window.NativeWindow() - if nativeWindow == nil { - return errors.New("window native handle unavailable") - } - hwnd := uintptr(nativeWindow) - - w.createBadge() - - var hicon w32.HICON - var err error - if label == "" { - hicon, err = w.createBadgeIcon() - if err != nil { - return err +func (w *windowsDock) SetBadge(label string) error { + return application.InvokeSyncWithError(func() error { + if w.taskbar == nil { + return nil } - } else { - hicon, err = w.createBadgeIconWithText(label) - if err != nil { - return err - } - } - defer w32.DestroyIcon(hicon) - return w.taskbar.SetOverlayIcon(hwnd, hicon, nil) + app := application.Get() + if app == nil { + return nil + } + + window := app.Window.Current() + if window == nil { + return nil + } + + nativeWindow := window.NativeWindow() + if nativeWindow == nil { + return errors.New("window native handle unavailable") + } + hwnd := uintptr(nativeWindow) + + w.createBadge() + + var hicon w32.HICON + var err error + if label == "" { + hicon, err = w.createBadgeIcon() + if err != nil { + return err + } + } else { + hicon, err = w.createBadgeIconWithText(label) + if err != nil { + return err + } + } + defer w32.DestroyIcon(hicon) + + return w.taskbar.SetOverlayIcon(hwnd, hicon, nil) + }) } // SetCustomBadge sets the badge label on the application icon with one-off options. -func (w *windowsBadge) SetCustomBadge(label string, options Options) error { - if w.taskbar == nil { - return nil - } +func (w *windowsDock) SetCustomBadge(label string, options BadgeOptions) error { + return application.InvokeSyncWithError(func() error { + if w.taskbar == nil { + return nil + } - app := application.Get() - if app == nil { - return nil - } + app := application.Get() + if app == nil { + return nil + } - window := app.Window.Current() - if window == nil { - return nil - } + window := app.Window.Current() + if window == nil { + return nil + } - nativeWindow := window.NativeWindow() - if nativeWindow == nil { - return errors.New("window native handle unavailable") - } - hwnd := uintptr(nativeWindow) + nativeWindow := window.NativeWindow() + if nativeWindow == nil { + return errors.New("window native handle unavailable") + } + hwnd := uintptr(nativeWindow) - const badgeSize = 32 + const badgeSize = 32 - img := image.NewRGBA(image.Rect(0, 0, badgeSize, badgeSize)) + img := image.NewRGBA(image.Rect(0, 0, badgeSize, badgeSize)) - backgroundColour := options.BackgroundColour - radius := badgeSize / 2 - centerX, centerY := radius, radius + backgroundColour := options.BackgroundColour + radius := badgeSize / 2 + centerX, centerY := radius, radius - for y := 0; y < badgeSize; y++ { - for x := 0; x < badgeSize; x++ { - dx := float64(x - centerX) - dy := float64(y - centerY) + for y := 0; y < badgeSize; y++ { + for x := 0; x < badgeSize; x++ { + dx := float64(x - centerX) + dy := float64(y - centerY) - if dx*dx+dy*dy < float64(radius*radius) { - img.Set(x, y, backgroundColour) + if dx*dx+dy*dy < float64(radius*radius) { + img.Set(x, y, backgroundColour) + } } } - } - var hicon w32.HICON - var err error - if label == "" { - hicon, err = createBadgeIcon(badgeSize, img, options) - if err != nil { - return err + var hicon w32.HICON + var err error + if label == "" { + hicon, err = createBadgeIcon(badgeSize, img, options) + if err != nil { + return err + } + } else { + hicon, err = createBadgeIconWithText(w, label, badgeSize, img, options) + if err != nil { + return err + } } - } else { - hicon, err = createBadgeIconWithText(w, label, badgeSize, img, options) - if err != nil { - return err - } - } - defer w32.DestroyIcon(hicon) + defer w32.DestroyIcon(hicon) - return w.taskbar.SetOverlayIcon(hwnd, hicon, nil) + return w.taskbar.SetOverlayIcon(hwnd, hicon, nil) + }) } // RemoveBadge removes the badge label from the application icon. -func (w *windowsBadge) RemoveBadge() error { - if w.taskbar == nil { - return nil - } +func (w *windowsDock) RemoveBadge() error { + return application.InvokeSyncWithError(func() error { + if w.taskbar == nil { + return nil + } - app := application.Get() - if app == nil { - return nil - } + app := application.Get() + if app == nil { + return nil + } - window := app.Window.Current() - if window == nil { - return nil - } + window := app.Window.Current() + if window == nil { + return nil + } - nativeWindow := window.NativeWindow() - if nativeWindow == nil { - return errors.New("window native handle unavailable") - } - hwnd := uintptr(nativeWindow) + nativeWindow := window.NativeWindow() + if nativeWindow == nil { + return errors.New("window native handle unavailable") + } + hwnd := uintptr(nativeWindow) - return w.taskbar.SetOverlayIcon(hwnd, 0, nil) + return w.taskbar.SetOverlayIcon(hwnd, 0, nil) + }) } // createBadgeIcon creates a badge icon with the specified size and color. -func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) { +func (w *windowsDock) createBadgeIcon() (w32.HICON, error) { radius := w.badgeSize / 2 centerX, centerY := radius, radius innerRadius := w.badgeSize / 5 @@ -209,7 +225,7 @@ func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) { dy := float64(y - centerY) if dx*dx+dy*dy < float64(innerRadius*innerRadius) { - w.badgeImg.Set(x, y, w.options.TextColour) + w.badgeImg.Set(x, y, w.badgeOptions.TextColour) } } } @@ -223,7 +239,7 @@ func (w *windowsBadge) createBadgeIcon() (w32.HICON, error) { return hicon, err } -func createBadgeIcon(badgeSize int, img *image.RGBA, options Options) (w32.HICON, error) { +func createBadgeIcon(badgeSize int, img *image.RGBA, options BadgeOptions) (w32.HICON, error) { radius := badgeSize / 2 centerX, centerY := radius, radius innerRadius := badgeSize / 5 @@ -249,8 +265,8 @@ func createBadgeIcon(badgeSize int, img *image.RGBA, options Options) (w32.HICON } // createBadgeIconWithText creates a badge icon with the specified text. -func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error) { - fontPath := w.fontManager.FindFontOrDefault(w.options.FontName) +func (w *windowsDock) createBadgeIconWithText(label string) (w32.HICON, error) { + fontPath := w.fontManager.FindFontOrDefault(w.badgeOptions.FontName) if fontPath == "" { return w.createBadgeIcon() } @@ -265,9 +281,9 @@ func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error) return w.createBadgeIcon() } - fontSize := float64(w.options.FontSize) + fontSize := float64(w.badgeOptions.FontSize) if len(label) > 1 { - fontSize = float64(w.options.SmallFontSize) + fontSize = float64(w.badgeOptions.SmallFontSize) } // Get DPI of the current screen @@ -286,7 +302,7 @@ func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error) d := &font.Drawer{ Dst: w.badgeImg, - Src: image.NewUniform(w.options.TextColour), + Src: image.NewUniform(w.badgeOptions.TextColour), Face: face, } @@ -302,7 +318,7 @@ func (w *windowsBadge) createBadgeIconWithText(label string) (w32.HICON, error) return w32.CreateSmallHIconFromImage(buf.Bytes()) } -func createBadgeIconWithText(w *windowsBadge, label string, badgeSize int, img *image.RGBA, options Options) (w32.HICON, error) { +func createBadgeIconWithText(w *windowsDock, label string, badgeSize int, img *image.RGBA, options BadgeOptions) (w32.HICON, error) { fontPath := w.fontManager.FindFontOrDefault(options.FontName) if fontPath == "" { return createBadgeIcon(badgeSize, img, options) @@ -356,12 +372,12 @@ func createBadgeIconWithText(w *windowsBadge, label string, badgeSize int, img * } // createBadge creates a circular badge with the specified background color. -func (w *windowsBadge) createBadge() { +func (w *windowsDock) createBadge() { w.badgeSize = 32 img := image.NewRGBA(image.Rect(0, 0, w.badgeSize, w.badgeSize)) - backgroundColour := w.options.BackgroundColour + backgroundColour := w.badgeOptions.BackgroundColour radius := w.badgeSize / 2 centerX, centerY := radius, radius diff --git a/v3/pkg/services/badge/font.go b/v3/pkg/services/dock/font.go similarity index 99% rename from v3/pkg/services/badge/font.go rename to v3/pkg/services/dock/font.go index 430a9c14d..54dcc8cd6 100644 --- a/v3/pkg/services/badge/font.go +++ b/v3/pkg/services/dock/font.go @@ -1,6 +1,6 @@ //go:build windows -package badge +package dock import ( "errors"