diff --git a/mkdocs-website/docs/en/changelog.md b/mkdocs-website/docs/en/changelog.md index b705924b9..570a25695 100644 --- a/mkdocs-website/docs/en/changelog.md +++ b/mkdocs-website/docs/en/changelog.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - [windows] Window class name option by [windom](https://github.com/windom/) in [#3682](https://github.com/wailsapp/wails/pull/3682) +- Services have been expanded to provide plugin functionality. By [atterpac](https://github.com/atterpac) and [leaanthony](https://github.com/leaanthony) in [#3570](https://github.com/wailsapp/wails/pull/3570) ### Fixed - [windows] Fixed syso icon file generation bug by [atterpac](https://github.com/atterpac) in [#3675](https://github.com/wailsapp/wails/pull/3675) diff --git a/mkdocs-website/docs/en/learn/services.md b/mkdocs-website/docs/en/learn/services.md new file mode 100644 index 000000000..9d98da719 --- /dev/null +++ b/mkdocs-website/docs/en/learn/services.md @@ -0,0 +1,145 @@ +# Services + +Services in Wails v3 provide a powerful way to extend the functionality of your application. They allow you to create +modular, reusable components that can be easily integrated into your Wails application. + +## Overview + +Services are designed to encapsulate specific functionality and can be registered with the application at startup. +They can handle various tasks such as file serving, database operations, logging, and more. +Services can also interact with the application lifecycle and respond to HTTP requests. + +## Creating a Service + +To create a service, you simply define a struct. Here's a basic structure of a service: + +```go +type MyService struct { + // Your service fields +} + +func NewMyService() *MyService { + // Initialize and return your service +} + +func (s *MyService) Greet(name string) string { + return fmt.Sprintf("Hello, %s!", name) +} +``` + +This service has a single method, `Greet`, which accepts a name and returns a greeting. + +## Registering a Service + +To register a service with the application, you need to provide an instance of the service to the `Services` field of +the `application.Options` struct (All services need to be wrapped by an `application.NewService` call. Here's an example: + +```go +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(NewMyService()), + }, +}) + +``` + +## Optional Methods + +Services can implement optional methods to hook into the application lifecycle: + +### Name + +```go +func (s *Service) Name() string +``` + +This method returns the name of the service. It is used for logging purposes only. + +### OnStartup + +```go +func (s *Service) OnStartup(ctx context.Context, options application.ServiceOptions) error +``` + +This method is called when the application is starting up. You can use it to initialize resources, set up connections, +or perform any necessary setup tasks. The context is the application context, and the `options` parameter provides +additional information about the service. + +### OnShutdown + +```go +func (s *Service) OnShutdown() error +``` + +This method is called when the application is shutting down. Use it to clean up resources, close connections, or +perform any necessary cleanup tasks. + +### ServeHTTP + +```go +func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) +``` + +If your service needs to handle HTTP requests, implement this method. It allows your service to act as an HTTP handler. +The route of the handler is defined in the service options: + +```go + application.NewService(fileserver.New(&fileserver.Config{ + RootPath: rootPath, + }), application.ServiceOptions{ + Route: "/files", + }), +``` + +## Example: File Server Service + +Let's look at a simplified version of the `fileserver` service as an example: + +```go +type Service struct { + config *Config + fs http.Handler +} + +func New(config *Config) *Service { + return &Service{ + config: config, + fs: http.FileServer(http.Dir(config.RootPath)), + } +} + +func (s *Service) Name() string { + return "github.com/wailsapp/wails/v3/services/fileserver" +} + +func (s *Service) OnStartup(ctx context.Context, options application.ServiceOptions) error { + // Any initialization code here + return nil +} + +func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.fs.ServeHTTP(w, r) +} +``` + +We can now use this service in our application: + +```go +app := application.New(application.Options{ + Services: []application.Service{ + application.NewService(fileserver.New(&fileserver.Config{ + RootPath: rootPath, + }), application.ServiceOptions{ + Route: "/files", + }), +``` +All requests to `/files` will be handled by the `fileserver` service. + +## Application Lifecycle and Services + +1. During application initialization, services are registered with the application. +2. When the application starts (`app.Run()`), the `OnStartup` method of each service is called with the application + context and service options. +3. Throughout the application's lifetime, services can perform their specific tasks. +4. If a service implements `ServeHTTP`, it can handle HTTP requests at the specified path. +5. When the application is shutting down, the `OnShutdown` method of each service is called as well as the context being cancelled. diff --git a/mkdocs-website/mkdocs.yml b/mkdocs-website/mkdocs.yml index f4596a694..92703d881 100644 --- a/mkdocs-website/mkdocs.yml +++ b/mkdocs-website/mkdocs.yml @@ -143,6 +143,7 @@ nav: - Your First Application: getting-started/your-first-app.md - Next Steps: getting-started/next-steps.md - Learn More: + - Services: learn/services.md - Runtime: learn/runtime.md - Plugins: learn/plugins.md - Guides: diff --git a/v3/cmd/wails3/main.go b/v3/cmd/wails3/main.go index c49ed56eb..cd9f520a6 100644 --- a/v3/cmd/wails3/main.go +++ b/v3/cmd/wails3/main.go @@ -57,10 +57,8 @@ func main() { generate.NewSubCommandFunction(".desktop", "Generate .desktop file", commands.GenerateDotDesktop) generate.NewSubCommandFunction("appimage", "Generate Linux AppImage", commands.GenerateAppImage) - plugin := app.NewSubCommand("plugin", "Plugin tools") - //plugin.NewSubCommandFunction("list", "List plugins", commands.PluginList) - plugin.NewSubCommandFunction("init", "Initialise a new plugin", commands.PluginInit) - //plugin.NewSubCommandFunction("add", "Add a plugin", commands.PluginAdd) + plugin := app.NewSubCommand("service", "Service tools") + plugin.NewSubCommandFunction("init", "Initialise a new service", commands.ServiceInit) tool := app.NewSubCommand("tool", "Various tools") tool.NewSubCommandFunction("checkport", "Checks if a port is open. Useful for testing if vite is running.", commands.ToolCheckPort) tool.NewSubCommandFunction("watcher", "Watches files and runs a command when they change", commands.Watcher) diff --git a/v3/examples/plugins/README.md b/v3/examples/plugins/README.md deleted file mode 100644 index 8b51a0568..000000000 --- a/v3/examples/plugins/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Plugins Example - -This example is not ready for testing yet. \ No newline at end of file diff --git a/v3/examples/plugins/Taskfile.yml b/v3/examples/plugins/Taskfile.yml deleted file mode 100644 index 01d8f2efc..000000000 --- a/v3/examples/plugins/Taskfile.yml +++ /dev/null @@ -1,42 +0,0 @@ -version: '3' - -tasks: - - pre-build: - summary: Pre-build hooks - - post-build: - summary: Post-build hooks - - build: - summary: Builds the application - cmds: - - task: pre-build - - go build -gcflags=all="-N -l" -o bin/testapp main.go - - task: post-build - env: - CGO_CFLAGS: "-mmacosx-version-min=10.13" - CGO_LDFLAGS: "-mmacosx-version-min=10.13" - MACOSX_DEPLOYMENT_TARGET: "10.13" - - generate-icons: - summary: Generates Windows `.ico` and Mac `.icns` files from an image - cmds: - # Generates both .ico and .icns files - - wails generate icons -input build/appicon.png - - build-prod: - summary: Creates a production build of the application - cmds: - - go build -tags production -ldflags="-w -s" -o bin/testapp - - package-darwin: - summary: Packages a production build of the application into a `.app` bundle - deps: - - build-prod - - generate-icons - cmds: - - mkdir -p buildtest.app/Contents/{MacOS,Resources} - - cp build/icons.icns buildtest.app/Contents/Resources - - cp bin/testapp buildtest.app/Contents/MacOS - - cp build/Info.plist buildtest.app/Contents \ No newline at end of file diff --git a/v3/examples/plugins/build/Info.dev.plist b/v3/examples/plugins/build/Info.dev.plist deleted file mode 100644 index d6d28b179..000000000 --- a/v3/examples/plugins/build/Info.dev.plist +++ /dev/null @@ -1,35 +0,0 @@ - - - - CFBundlePackageType - APPL - CFBundleName - My App - CFBundleExecutable - app - CFBundleIdentifier - com.wails.app - CFBundleVersion - v1.0.0 - CFBundleGetInfoString - The ultimate thing - CFBundleShortVersionString - v1 - CFBundleIconFile - icons - LSMinimumSystemVersion - 10.13.0 - NSHighResolutionCapable - true - NSHumanReadableCopyright - (c) Me - NSAppTransportSecurity - - NSAllowsLocalNetworking - - - - - - - \ No newline at end of file diff --git a/v3/examples/plugins/build/Info.plist b/v3/examples/plugins/build/Info.plist deleted file mode 100644 index ab571ad4f..000000000 --- a/v3/examples/plugins/build/Info.plist +++ /dev/null @@ -1,27 +0,0 @@ - - - - CFBundlePackageType - APPL - CFBundleName - My App - CFBundleExecutable - testapp - CFBundleIdentifier - com.wails.app - CFBundleVersion - v1.0.0 - CFBundleGetInfoString - The ultimate thing - CFBundleShortVersionString - v1 - CFBundleIconFile - icons - LSMinimumSystemVersion - 10.13.0 - NSHighResolutionCapable - true - NSHumanReadableCopyright - (c) Me - - \ No newline at end of file diff --git a/v3/examples/plugins/build/appicon.png b/v3/examples/plugins/build/appicon.png deleted file mode 100644 index 63617fe4f..000000000 Binary files a/v3/examples/plugins/build/appicon.png and /dev/null differ diff --git a/v3/examples/plugins/build/icons.icns b/v3/examples/plugins/build/icons.icns deleted file mode 100644 index e69de29bb..000000000 diff --git a/v3/examples/plugins/build/icons.ico b/v3/examples/plugins/build/icons.ico deleted file mode 100644 index e69de29bb..000000000 diff --git a/v3/examples/plugins/build/info.json b/v3/examples/plugins/build/info.json deleted file mode 100644 index 1005eb5cb..000000000 --- a/v3/examples/plugins/build/info.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "fixed": { - "file_version": "v1.0.0" - }, - "info": { - "0000": { - "ProductVersion": "v1.0.0", - "CompanyName": "My Company Name", - "FileDescription": "A thing that does a thing", - "LegalCopyright": "(c) 2023 My Company Name", - "ProductName": "My Product Name", - "Comments": "This is a comment" - } - } -} \ No newline at end of file diff --git a/v3/examples/plugins/build/wails.exe.manifest b/v3/examples/plugins/build/wails.exe.manifest deleted file mode 100644 index fb1ce5bde..000000000 --- a/v3/examples/plugins/build/wails.exe.manifest +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - true/pm - permonitorv2,permonitor - - - \ No newline at end of file diff --git a/v3/examples/plugins/go.mod b/v3/examples/plugins/go.mod deleted file mode 100644 index 904976bf1..000000000 --- a/v3/examples/plugins/go.mod +++ /dev/null @@ -1,65 +0,0 @@ -module plugin_demo - -go 1.22 - -toolchain go1.22.0 - -require github.com/wailsapp/wails/v3 v3.0.0-alpha.0 - -require ( - dario.cat/mergo v1.0.0 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect - github.com/bep/debounce v1.2.1 // indirect - github.com/cloudflare/circl v1.3.7 // indirect - github.com/cyphar/filepath-securejoin v0.2.4 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/ebitengine/purego v0.4.0-alpha.4 // indirect - github.com/emirpasic/gods v1.18.1 // indirect - github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.5.0 // indirect - github.com/go-git/go-git/v5 v5.11.0 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/leaanthony/go-ansi-parser v1.6.1 // indirect - github.com/leaanthony/u v1.1.0 // indirect - github.com/lmittmann/tint v1.0.3 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/pjbgf/sha1cd v0.3.0 // indirect - github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/samber/lo v1.38.1 // indirect - github.com/sergi/go-diff v1.2.0 // indirect - github.com/skeema/knownhosts v1.2.1 // indirect - github.com/wailsapp/go-webview2 v1.0.9 // indirect - github.com/wailsapp/mimetype v1.4.1 // indirect - github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/tools v0.13.0 // indirect - gopkg.in/warnings.v0 v0.1.2 // indirect - lukechampine.com/uint128 v1.2.0 // indirect - modernc.org/cc/v3 v3.40.0 // indirect - modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.22.3 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.5.0 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.21.0 // indirect - modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.1 // indirect -) - -replace github.com/wailsapp/wails/v3 => ../.. diff --git a/v3/examples/plugins/go.sum b/v3/examples/plugins/go.sum deleted file mode 100644 index 1b9020e45..000000000 --- a/v3/examples/plugins/go.sum +++ /dev/null @@ -1,227 +0,0 @@ -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= -github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= -github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/ebitengine/purego v0.4.0-alpha.4 h1:Y7yIV06Yo5M2BAdD7EVPhfp6LZ0tEcQo5770OhYUVes= -github.com/ebitengine/purego v0.4.0-alpha.4/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= -github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= -github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= -github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= -github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= -github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A= -github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= -github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI= -github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= -github.com/lmittmann/tint v1.0.3 h1:W5PHeA2D8bBJVvabNfQD/XW9HPLZK1XoPZH0cq8NouQ= -github.com/lmittmann/tint v1.0.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= -github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= -github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= -github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/wailsapp/go-webview2 v1.0.9 h1:lrU+q0cf1wgLdR69rN+ZnRtMJNaJRrcQ4ELxoO7/xjs= -github.com/wailsapp/go-webview2 v1.0.9/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= -github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= -github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= -github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY= -modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= -modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.21.0 h1:4aP4MdUf15i3R3M2mx6Q90WHKz3nZLoz96zlB6tNdow= -modernc.org/sqlite v1.21.0/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws= -modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= -modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= diff --git a/v3/examples/plugins/hashes/README.md b/v3/examples/plugins/hashes/README.md deleted file mode 100644 index 074b08019..000000000 --- a/v3/examples/plugins/hashes/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Hashes Plugin - -This example plugin provides a way to generate hashes of strings. - -## Usage - -Add the plugin to the `Plugins` option in the Applications options: - -```go - Plugins: map[string]application.Plugin{ - "hashes": hashes.NewPlugin(), - }, -``` - -You can then call the Generate method from the frontend: - -```js - import {Call} from "/wails/runtime.js"; - Call.Plugin("hashes","Generate","hello world").then((result) => console.log(result)) -``` - -This method returns a struct with the following fields: - -```typescript - interface Hashes { - md5: string; - sha1: string; - sha256: string; - } -``` - -A TypeScript definition file is provided for this interface. diff --git a/v3/examples/plugins/hashes/plugin.go b/v3/examples/plugins/hashes/plugin.go deleted file mode 100644 index dcc02c25e..000000000 --- a/v3/examples/plugins/hashes/plugin.go +++ /dev/null @@ -1,58 +0,0 @@ -package hashes - -import ( - "crypto/md5" - "crypto/sha1" - "crypto/sha256" - "encoding/hex" - "github.com/wailsapp/wails/v3/pkg/application" - "io/fs" -) - -// ---------------- Plugin Setup ---------------- - -type Plugin struct{} - -func NewPlugin() *Plugin { - return &Plugin{} -} - -func (r *Plugin) Shutdown() error { return nil } - -func (r *Plugin) Name() string { - return "Hashes Plugin" -} - -func (r *Plugin) Init(api application.PluginAPI) error { - return nil -} - -func (r *Plugin) CallableByJS() []string { - return []string{ - "Generate", - } -} - -func (r *Plugin) Assets() fs.FS { - return nil -} - -// ---------------- Plugin Methods ---------------- - -type Hashes struct { - MD5 string `json:"md5"` - SHA1 string `json:"sha1"` - SHA256 string `json:"sha256"` -} - -func (r *Plugin) Generate(s string) Hashes { - md5Hash := md5.Sum([]byte(s)) - sha1Hash := sha1.Sum([]byte(s)) - sha256Hash := sha256.Sum256([]byte(s)) - - return Hashes{ - MD5: hex.EncodeToString(md5Hash[:]), - SHA1: hex.EncodeToString(sha1Hash[:]), - SHA256: hex.EncodeToString(sha256Hash[:]), - } -} diff --git a/v3/examples/plugins/hashes/plugin.toml b/v3/examples/plugins/hashes/plugin.toml deleted file mode 100644 index 7835721be..000000000 --- a/v3/examples/plugins/hashes/plugin.toml +++ /dev/null @@ -1,10 +0,0 @@ -# This is the plugin definition file for the "Hashes" plugin. - -Name = "Hashes" -Description = "Provides a method to generate a number of hashes." -Author = "Lea Anthony" -Version = "v1.0.0" -Website = "https://wails.io" -License = "MIT" - - diff --git a/v3/examples/plugins/icon.ico b/v3/examples/plugins/icon.ico deleted file mode 100644 index bfa0690b7..000000000 Binary files a/v3/examples/plugins/icon.ico and /dev/null differ diff --git a/v3/examples/plugins/icons.icns b/v3/examples/plugins/icons.icns deleted file mode 100644 index 1b5bd4c86..000000000 Binary files a/v3/examples/plugins/icons.icns and /dev/null differ diff --git a/v3/examples/plugins/main.go b/v3/examples/plugins/main.go deleted file mode 100644 index 44e4387ca..000000000 --- a/v3/examples/plugins/main.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "embed" - "log/slog" - "os" - "plugin_demo/hashes" - - "github.com/wailsapp/wails/v3/pkg/application" - "github.com/wailsapp/wails/v3/plugins/kvstore" - "github.com/wailsapp/wails/v3/plugins/log" - "github.com/wailsapp/wails/v3/plugins/single_instance" - "github.com/wailsapp/wails/v3/plugins/sqlite" - "github.com/wailsapp/wails/v3/plugins/start_at_login" -) - -//go:embed assets/* -var assets embed.FS - -func main() { - - app := application.New(application.Options{ - Name: "Plugin Demo", - Description: "A demo of the plugins API", - Mac: application.MacOptions{ - ApplicationShouldTerminateAfterLastWindowClosed: true, - }, - LogLevel: slog.LevelDebug, - Plugins: map[string]application.Plugin{ - "hashes": hashes.NewPlugin(), - "log": log.NewPlugin(), - "sqlite": sqlite.NewPlugin(&sqlite.Config{ - DBFile: "test.db", - }), - "kvstore": kvstore.NewPlugin(&kvstore.Config{ - Filename: "store.json", - AutoSave: true, - }), - "single_instance": single_instance.NewPlugin(&single_instance.Config{ - // When true, the original app will be activated when a second instance is launched - ActivateAppOnSubsequentLaunch: true, - }), - "start_at_login": start_at_login.NewPlugin(start_at_login.Config{}), - }, - Assets: application.AssetOptions{ - Handler: application.BundledAssetFileServer(assets), - }, - }) - - app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ - Width: 1024, - Height: 768, - }) - - err := app.Run() - - if err != nil { - println(err.Error()) - os.Exit(1) - } -} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/hashes.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/hashes.js new file mode 100644 index 000000000..d2aaa4fdd --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/hashes.js @@ -0,0 +1,35 @@ +// @ts-check +// 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 {Call as $Call, Create as $Create} from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as $models from "./models.js"; + +/** + * @param {string} s + * @returns {Promise<$models.Hashes> & { cancel(): void }} + */ +export function Generate(s) { + let $resultPromise = /** @type {any} */($Call.ByID(1640814231, s)); + let $typingPromise = /** @type {any} */($resultPromise.then(($result) => { + return $$createType0($result); + })); + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; +} + +/** + * @returns {Promise & { cancel(): void }} + */ +export function Name() { + let $resultPromise = /** @type {any} */($Call.ByID(2815914379)); + return $resultPromise; +} + +// Private type creation functions +const $$createType0 = $models.Hashes.createFrom; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/index.js new file mode 100644 index 000000000..defc166a5 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/index.js @@ -0,0 +1,10 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Hashes from "./hashes.js"; +export { + Hashes +}; + +export * from "./models.js"; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/models.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/models.js new file mode 100644 index 000000000..451164f5b --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/examples/services/hashes/models.js @@ -0,0 +1,49 @@ +// @ts-check +// 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 "/wails/runtime.js"; + +export class Hashes { + /** + * Creates a new Hashes instance. + * @param {Partial} [$$source = {}] - The source object to create the Hashes. + */ + constructor($$source = {}) { + if (!("md5" in $$source)) { + /** + * @member + * @type {string} + */ + this["md5"] = ""; + } + if (!("sha1" in $$source)) { + /** + * @member + * @type {string} + */ + this["sha1"] = ""; + } + if (!("sha256" in $$source)) { + /** + * @member + * @type {string} + */ + this["sha256"] = ""; + } + + Object.assign(this, $$source); + } + + /** + * Creates a new Hashes instance from a string or object. + * @param {any} [$$source = {}] + * @returns {Hashes} + */ + static createFrom($$source = {}) { + let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; + return new Hashes(/** @type {Partial} */($$parsedSource)); + } +} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/index.js new file mode 100644 index 000000000..c4e79bfd6 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/index.js @@ -0,0 +1,8 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as KeyValueStore from "./keyvaluestore.js"; +export { + KeyValueStore +}; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js new file mode 100644 index 000000000..912325534 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js @@ -0,0 +1,56 @@ +// @ts-check +// 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 {Call as $Call, Create as $Create} from "/wails/runtime.js"; + +/** + * Delete deletes the key from the store. If AutoSave is true, the store is saved to disk. + * @param {string} key + * @returns {Promise & { cancel(): void }} + */ +export function Delete(key) { + let $resultPromise = /** @type {any} */($Call.ByID(1029952841, key)); + return $resultPromise; +} + +/** + * Get returns the value for the given key. If key is empty, the entire store is returned. + * @param {string} key + * @returns {Promise & { cancel(): void }} + */ +export function Get(key) { + let $resultPromise = /** @type {any} */($Call.ByID(3017738442, key)); + return $resultPromise; +} + +/** + * Name returns the name of the plugin. + * @returns {Promise & { cancel(): void }} + */ +export function Name() { + let $resultPromise = /** @type {any} */($Call.ByID(2879709053)); + return $resultPromise; +} + +/** + * Save saves the store to disk + * @returns {Promise & { cancel(): void }} + */ +export function Save() { + let $resultPromise = /** @type {any} */($Call.ByID(840897339)); + return $resultPromise; +} + +/** + * Set sets the value for the given key. If AutoSave is true, the store is saved to disk. + * @param {string} key + * @param {any} value + * @returns {Promise & { cancel(): void }} + */ +export function Set(key, value) { + let $resultPromise = /** @type {any} */($Call.ByID(2329265830, key, value)); + return $resultPromise; +} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js new file mode 100644 index 000000000..d3c050f93 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js @@ -0,0 +1,8 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as LoggerService from "./loggerservice.js"; +export { + LoggerService +}; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js new file mode 100644 index 000000000..89d2c4456 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js @@ -0,0 +1,70 @@ +// @ts-check +// 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 {Call as $Call, Create as $Create} from "/wails/runtime.js"; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import * as slog$0 from "../../../../../../../log/slog/models.js"; + +/** + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +export function Debug(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(1384012895, message, args)); + return $resultPromise; +} + +/** + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +export function Error(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(1324251502, message, args)); + return $resultPromise; +} + +/** + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +export function Info(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(3712350036, message, args)); + return $resultPromise; +} + +/** + * Name returns the name of the plugin. + * You should use the go module format e.g. github.com/myuser/myplugin + * @returns {Promise & { cancel(): void }} + */ +export function Name() { + let $resultPromise = /** @type {any} */($Call.ByID(3407342027)); + return $resultPromise; +} + +/** + * @param {slog$0.Level} level + * @returns {Promise & { cancel(): void }} + */ +export function SetLogLevel(level) { + let $resultPromise = /** @type {any} */($Call.ByID(2521579448, level)); + return $resultPromise; +} + +/** + * @param {string} message + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +export function Warning(message, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(2902024404, message, args)); + return $resultPromise; +} diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js new file mode 100644 index 000000000..d2bf43c94 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js @@ -0,0 +1,8 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as Service from "./service.js"; +export { + Service +}; diff --git a/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js new file mode 100644 index 000000000..8821e8466 --- /dev/null +++ b/v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js @@ -0,0 +1,72 @@ +// @ts-check +// 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 {Call as $Call, Create as $Create} from "/wails/runtime.js"; + +/** + * @returns {Promise & { cancel(): void }} + */ +export function Close() { + let $resultPromise = /** @type {any} */($Call.ByID(1888105376)); + return $resultPromise; +} + +/** + * @param {string} query + * @param {any[]} args + * @returns {Promise & { cancel(): void }} + */ +export function Execute(query, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(3811930203, query, args)); + return $resultPromise; +} + +/** + * Name returns the name of the plugin. + * You should use the go module format e.g. github.com/myuser/myplugin + * @returns {Promise & { cancel(): void }} + */ +export function Name() { + let $resultPromise = /** @type {any} */($Call.ByID(2075046103)); + return $resultPromise; +} + +/** + * @param {string} dbPath + * @returns {Promise & { cancel(): void }} + */ +export function Open(dbPath) { + let $resultPromise = /** @type {any} */($Call.ByID(2012175612, dbPath)); + return $resultPromise; +} + +/** + * @param {string} query + * @param {any[]} args + * @returns {Promise<{ [_: string]: any }[]> & { cancel(): void }} + */ +export function Select(query, ...args) { + let $resultPromise = /** @type {any} */($Call.ByID(2472933124, query, args)); + let $typingPromise = /** @type {any} */($resultPromise.then(($result) => { + return $$createType1($result); + })); + $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); + return $typingPromise; +} + +/** + * Shutdown is called when the app is shutting down + * You can use this to clean up any resources you have allocated + * @returns {Promise & { cancel(): void }} + */ +export function Shutdown() { + let $resultPromise = /** @type {any} */($Call.ByID(846401686)); + return $resultPromise; +} + +// Private type creation functions +const $$createType0 = $Create.Map($Create.Any, $Create.Any); +const $$createType1 = $Create.Array($$createType0); diff --git a/v3/examples/services/assets/bindings/log/slog/index.js b/v3/examples/services/assets/bindings/log/slog/index.js new file mode 100644 index 000000000..580fb67af --- /dev/null +++ b/v3/examples/services/assets/bindings/log/slog/index.js @@ -0,0 +1,5 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export * from "./models.js"; diff --git a/v3/examples/services/assets/bindings/log/slog/models.js b/v3/examples/services/assets/bindings/log/slog/models.js new file mode 100644 index 000000000..ec976569d --- /dev/null +++ b/v3/examples/services/assets/bindings/log/slog/models.js @@ -0,0 +1,13 @@ +// @ts-check +// 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 "/wails/runtime.js"; + +/** + * A Level is the importance or severity of a log event. + * The higher the level, the more important or severe the event. + * @typedef {any} Level + */ diff --git a/v3/examples/plugins/assets/index.html b/v3/examples/services/assets/index.html similarity index 92% rename from v3/examples/plugins/assets/index.html rename to v3/examples/services/assets/index.html index 1fa889fba..b46b70832 100644 --- a/v3/examples/plugins/assets/index.html +++ b/v3/examples/services/assets/index.html @@ -6,15 +6,15 @@ -

Plugins

+

Services

@@ -232,14 +232,13 @@
-
-

The sqlite plugin provides easy integration with sqlite dbs.

+

The sqlite service provides easy integration with sqlite dbs.

The demo DB has a single table: Users.

Enter a query below and hit the "Run" button.

-
+
@@ -247,7 +246,7 @@
-

The hashes plugin provides hashing functions.

+

The hashes service provides hashing functions.


@@ -258,7 +257,7 @@
-

The kvstore plugin provides a means for reading and writing to a json file.

+

The kvstore service provides a means for reading and writing to a json file.

Enter a key/value pair in the form below to add it to the file.

A blank value will remove the key.

diff --git a/v3/examples/plugins/assets/style.css b/v3/examples/services/assets/style.css similarity index 93% rename from v3/examples/plugins/assets/style.css rename to v3/examples/services/assets/style.css index a32ce2879..f128a1aa1 100644 --- a/v3/examples/plugins/assets/style.css +++ b/v3/examples/services/assets/style.css @@ -7,6 +7,8 @@ html { -moz-user-select: none; -ms-user-select: none; -webkit-touch-callout: none; + height: 100vh; + width: 100%; } body { @@ -16,6 +18,15 @@ body { "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; overscroll-behavior: none; + overflow-y: hidden; + background-image: url("/files/images/eryri1.png"); + background-color: rgba(33, 37, 43, 0.85); + background-blend-mode: overlay; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + height: 100vh; + width: 100%; } .logo { diff --git a/v3/examples/services/files/images/eryri1.png b/v3/examples/services/files/images/eryri1.png new file mode 100644 index 000000000..224d3b4ac Binary files /dev/null and b/v3/examples/services/files/images/eryri1.png differ diff --git a/v3/examples/services/hashes/hashes.go b/v3/examples/services/hashes/hashes.go new file mode 100644 index 000000000..f7bbed7eb --- /dev/null +++ b/v3/examples/services/hashes/hashes.go @@ -0,0 +1,40 @@ +package hashes + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "encoding/hex" +) + +type Hashes struct { + MD5 string `json:"md5"` + SHA1 string `json:"sha1"` + SHA256 string `json:"sha256"` +} + +func (h *Hashes) Generate(s string) Hashes { + md5Hash := md5.Sum([]byte(s)) + sha1Hash := sha1.Sum([]byte(s)) + sha256Hash := sha256.Sum256([]byte(s)) + + return Hashes{ + MD5: hex.EncodeToString(md5Hash[:]), + SHA1: hex.EncodeToString(sha1Hash[:]), + SHA256: hex.EncodeToString(sha256Hash[:]), + } +} + +func New() *Hashes { + return &Hashes{} +} + +func (h *Hashes) OnShutdown() error { return nil } + +func (h *Hashes) Name() string { + return "Hashes Service" +} + +func (h *Hashes) OnStartup() error { + return nil +} diff --git a/v3/examples/services/main.go b/v3/examples/services/main.go new file mode 100644 index 000000000..d0f8ea582 --- /dev/null +++ b/v3/examples/services/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "embed" + "github.com/wailsapp/wails/v3/examples/services/hashes" + "github.com/wailsapp/wails/v3/pkg/application" + "github.com/wailsapp/wails/v3/pkg/services/fileserver" + "github.com/wailsapp/wails/v3/pkg/services/kvstore" + "github.com/wailsapp/wails/v3/pkg/services/log" + "github.com/wailsapp/wails/v3/pkg/services/sqlite" + "log/slog" + "os" + "path/filepath" +) + +//go:embed assets/* +var assets embed.FS + +func main() { + + rootPath, _ := filepath.Abs("./files") + app := application.New(application.Options{ + Name: "Services Demo", + Description: "A demo of the services API", + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + LogLevel: slog.LevelDebug, + Services: []application.Service{ + application.NewService(hashes.New()), + application.NewService(sqlite.New(&sqlite.Config{ + DBFile: "test.db", + })), + application.NewService(kvstore.New(&kvstore.Config{ + Filename: "store.json", + AutoSave: true, + })), + application.NewService(log.New()), + application.NewService(fileserver.New(&fileserver.Config{ + RootPath: rootPath, + }), application.ServiceOptions{ + Route: "/files", + }), + }, + Assets: application.AssetOptions{ + Handler: application.BundledAssetFileServer(assets), + }, + }) + + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + Width: 1024, + Height: 768, + }) + + err := app.Run() + + if err != nil { + println(err.Error()) + os.Exit(1) + } +} diff --git a/v3/examples/plugins/store.json b/v3/examples/services/store.json similarity index 100% rename from v3/examples/plugins/store.json rename to v3/examples/services/store.json diff --git a/v3/examples/plugins/test.db b/v3/examples/services/test.db similarity index 100% rename from v3/examples/plugins/test.db rename to v3/examples/services/test.db diff --git a/v3/internal/assetserver/assetserver.go b/v3/internal/assetserver/assetserver.go index b931d3d92..a52fe3c93 100644 --- a/v3/internal/assetserver/assetserver.go +++ b/v3/internal/assetserver/assetserver.go @@ -1,14 +1,11 @@ package assetserver import ( - "embed" "fmt" - "io/fs" "net" "net/http" "net/http/httptest" "net/url" - "path" "strings" "time" ) @@ -16,7 +13,7 @@ import ( const ( webViewRequestHeaderWindowId = "x-wails-window-id" webViewRequestHeaderWindowName = "x-wails-window-name" - pluginPrefix = "/wails/plugin" + servicePrefix = "wails/services" ) type RuntimeHandler interface { @@ -28,16 +25,14 @@ type AssetServer struct { handler http.Handler - //pluginScripts map[string]string + services map[string]http.Handler assetServerWebView - pluginAssets map[string]fs.FS } func NewAssetServer(options *Options) (*AssetServer, error) { result := &AssetServer{ - options: options, - pluginAssets: make(map[string]fs.FS), + options: options, } userHandler := options.Handler @@ -113,39 +108,35 @@ func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userH default: - // Check if this is a plugin asset - if !strings.HasPrefix(reqPath, pluginPrefix) { - userHandler.ServeHTTP(rw, req) - return - } - - // Ensure there is 4 parts to the reqPath - parts := strings.SplitN(reqPath, "/", 5) - if len(parts) < 5 { - rw.WriteHeader(http.StatusNotFound) - return - } - - // Get the first 3 parts of the reqPath - pluginPath := "/" + path.Join(parts[1], parts[2], parts[3]) - // Get the remaining part of the reqPath - fileName := parts[4] - - // Check if this is a registered plugin asset - if assetFS, ok := a.pluginAssets[pluginPath]; ok { - // Check if the file exists - file, err := fs.ReadFile(assetFS, fileName) - if err != nil { - a.serveError(rw, err, "Unable to read file %s", reqPath) + // Check if the path matches the keys in the services map + for route, handler := range a.services { + if strings.HasPrefix(reqPath, route) { + req.URL.Path = strings.TrimPrefix(reqPath, route) + // Strip leading slash + req.URL.Path = strings.TrimPrefix(req.URL.Path, "/") + handler.ServeHTTP(rw, req) return } - a.writeBlob(rw, reqPath, file) - } else { - userHandler.ServeHTTP(rw, req) } + + // Check if it can be served by the user-provided handler + if !strings.HasPrefix(reqPath, servicePrefix) { + userHandler.ServeHTTP(rw, req) + return + } + + rw.WriteHeader(http.StatusNotFound) + return } } +func (a *AssetServer) AttachServiceHandler(prefix string, handler http.Handler) { + if a.services == nil { + a.services = make(map[string]http.Handler) + } + a.services[prefix] = handler +} + func (a *AssetServer) writeBlob(rw http.ResponseWriter, filename string, blob []byte) { err := ServeFile(rw, filename, blob) if err != nil { @@ -159,30 +150,6 @@ func (a *AssetServer) serveError(rw http.ResponseWriter, err error, msg string, rw.WriteHeader(http.StatusInternalServerError) } -//func (a *AssetServer) AddPluginScript(pluginName string, script string) { -// if a.pluginScripts == nil { -// a.pluginScripts = make(map[string]string) -// } -// pluginName = strings.ReplaceAll(pluginName, "/", "_") -// pluginName = html.EscapeString(pluginName) -// pluginScriptName := fmt.Sprintf("/wails/plugin/%s.js", pluginName) -// a.pluginScripts[pluginScriptName] = script -//} - -func (a *AssetServer) AddPluginAssets(pluginPath string, vfs fs.FS) error { - pluginPath = path.Join(pluginPrefix, pluginPath) - _, exists := a.pluginAssets[pluginPath] - if exists { - return fmt.Errorf("plugin path already exists: %s", pluginPath) - } - if embedFs, isEmbedFs := vfs.(embed.FS); isEmbedFs { - rootFolder, _ := findEmbedRootPath(embedFs) - vfs, _ = fs.Sub(vfs, path.Clean(rootFolder)) - } - a.pluginAssets[pluginPath] = vfs - return nil -} - func GetStartURL(userURL string) (string, error) { devServerURL := GetDevServerURL() startURL := baseURL.String() diff --git a/v3/internal/commands/plugins.go b/v3/internal/commands/service.go similarity index 83% rename from v3/internal/commands/plugins.go rename to v3/internal/commands/service.go index a42790ab3..8babf87f3 100644 --- a/v3/internal/commands/plugins.go +++ b/v3/internal/commands/service.go @@ -2,7 +2,7 @@ package commands import ( "github.com/wailsapp/wails/v3/internal/flags" - "github.com/wailsapp/wails/v3/internal/plugins" + "github.com/wailsapp/wails/v3/internal/service" "strings" "github.com/pterm/pterm" @@ -31,7 +31,7 @@ func toCamelCasePlugin(s string) string { return camelCase + "Plugin" } -func PluginInit(options *flags.PluginInit) error { +func ServiceInit(options *flags.ServiceInit) error { if options.Quiet { pterm.DisableOutput() @@ -41,5 +41,5 @@ func PluginInit(options *flags.PluginInit) error { options.PackageName = toCamelCasePlugin(options.Name) } - return plugins.Install(options) + return service.Install(options) } diff --git a/v3/internal/flags/plugin.go b/v3/internal/flags/service.go similarity index 51% rename from v3/internal/flags/plugin.go rename to v3/internal/flags/service.go index 62c0dd968..c52accd87 100644 --- a/v3/internal/flags/plugin.go +++ b/v3/internal/flags/service.go @@ -1,9 +1,14 @@ package flags -type PluginInit struct { +type ServiceInit struct { Name string `name:"n" description:"Name of plugin" default:"example_plugin"` Description string `name:"d" description:"Description of plugin" default:"Example plugin"` PackageName string `name:"p" description:"Package name for plugin" default:""` OutputDir string `name:"o" description:"Output directory" default:"."` Quiet bool `name:"q" description:"Suppress output to console"` + Author string `name:"a" description:"Author of plugin" default:""` + Version string `name:"v" description:"Version of plugin" default:""` + Website string `name:"w" description:"Website of plugin" default:""` + Repository string `name:"r" description:"Repository of plugin" default:""` + License string `name:"l" description:"License of plugin" default:""` } diff --git a/v3/internal/generator/analyse.go b/v3/internal/generator/analyse.go index a0a56bca1..6aca7b899 100644 --- a/v3/internal/generator/analyse.go +++ b/v3/internal/generator/analyse.go @@ -120,7 +120,8 @@ func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, log if fn.Name() == "NewService" && fn.Pkg().Path() == systemPaths.ApplicationPackage { // Check signature. signature := fn.Type().(*types.Signature) - if signature.Params().Len() != 1 || signature.Results().Len() != 1 || tp.Len() != 1 || tp.At(0).Obj() == nil { + if signature.Params().Len() > 2 || signature.Results().Len() != 1 || tp.Len() != 1 || tp.At(0).Obj() == nil { + logger.Warningf("Param Len: %d, Results Len: %d, tp.Len: %d, tp.At(0).Obj(): %v", signature.Params().Len(), signature.Results().Len(), tp.Len(), tp.At(0).Obj()) return ErrBadApplicationPackage } diff --git a/v3/internal/generator/collect/service.go b/v3/internal/generator/collect/service.go index fc0d30a8c..007b92ba5 100644 --- a/v3/internal/generator/collect/service.go +++ b/v3/internal/generator/collect/service.go @@ -54,6 +54,22 @@ type ( } ) +func isInternalServiceMethod(method *types.Func) bool { + internalServiceMethods := []string{ + "OnStartup", + "OnShutdown", + "ServeHTTP", + } + methodName := method.Name() + for _, name := range internalServiceMethods { + if name == methodName { + return true + } + } + + return false +} + func newServiceInfo(collector *Collector, obj *types.TypeName) *ServiceInfo { return &ServiceInfo{ TypeInfo: collector.Type(obj), @@ -171,6 +187,10 @@ var typeAny = types.Universe.Lookup("any").Type().Underlying() // collectMethod collects and returns information about a service method. // It is intended to be called only by ServiceInfo.Collect. func (info *ServiceInfo) collectMethod(method *types.Func) *ServiceMethodInfo { + if isInternalServiceMethod(method) { + // Ignore internal methods. + return nil + } collector := info.collector obj := info.Object().(*types.TypeName) diff --git a/v3/internal/plugins/template/NEXT STEPS.md b/v3/internal/plugins/template/NEXT STEPS.md deleted file mode 100644 index e58c6b119..000000000 --- a/v3/internal/plugins/template/NEXT STEPS.md +++ /dev/null @@ -1,82 +0,0 @@ -# Next Steps - -Congratulations on generating a plugin. This guide will help you author your plugin -and provide some tips on how to get started. - -## Plugin Structure - -The plugin is a standard Go module that adheres to the following interface: - -```go -type Plugin interface { - Name() string - Init(app *App) error - Shutdown() -} -``` - -The `Name()` method returns the name of the plugin. It should follow the Go module naming convention -and have a prefix of `wails-plugin-`, e.g. `github.com/myuser/wails-plugin-example`. - -The `Init()` method is called when the plugin is loaded. The `App` parameter is a pointer to the -main application struct. This may be used for showing dialogs, listening for events or even opening -new windows. The `Init()` method should return an error if it fails to initialise. This method is -called synchronously so the application will not start until it returns. - -The `Shutdown()` method is called when the application is shutting down. This is a good place to -perform any cleanup. This method is called synchronously so the application will not exit completely until -it returns. - -## Plugin Directory Structure - -The plugin directory structure is as follows: - -``` -plugin-name -├── models.d.ts -├── plugin.js -├── plugin.go -├── README.md -├── go.mod -├── go.sum -└── plugin.toml -``` - -### `plugin.go` - -This file contains the plugin code. It should contain a struct that implements the `Plugin` interface -and a `NewPlugin()` method that returns a pointer to the struct. Methods are exported by capitalising -the first letter of the method name. These methods may be called from the frontend. If methods -accept or return structs, these structs must be exported. - -### `plugin.js` - -This file should contain any JavaScript code that may help developers use the plugin. -In the example plugin, this file contains function wrappers for the plugin methods. -It's good to include JSDocs as that will help developers using your plugin. - -### `models.d.ts` - -This file should contain TypeScript definitions for any structs that are passed -or returned from the plugin. -` -### `plugin.toml` - -This file contains the plugin metadata. It is important to fill this out correctly -as it will be used by the Wails CLI. - -### `README.md` - -This file should contain a description of the plugin and how to use it. It should -also contain a link to the plugin repository and how to report bugs. - -### `go.mod` and `go.sum` - -These are standard Go module files. The package name in `go.mod` should match the -name of the plugin, e.g. `github.com/myuser/wails-plugin-example`. - -## Promoting your Plugin - -Once you have created your plugin, you should promote it on the Wails Discord server -in the `#plugins` channel. You should also open a PR to promote your plugin on the Wails -website. Update the `website/content/plugins.md` file and add your plugin to the list. \ No newline at end of file diff --git a/v3/internal/plugins/template/README.tmpl.md b/v3/internal/plugins/template/README.tmpl.md deleted file mode 100644 index 2f4944a3d..000000000 --- a/v3/internal/plugins/template/README.tmpl.md +++ /dev/null @@ -1,38 +0,0 @@ -# {{.Name}} Plugin - -This example plugin provides a way to generate hashes of strings. - -## Installation - -Add the plugin to the `Plugins` option in the Applications options: - -```go - Plugins: map[string]application.Plugin{ - "{{.Name}}": {{.Name}}.NewPlugin(), - }, -``` - -## Usage - -You can then call the methods from the frontend: - -```js - wails.Plugin("{{.Name}}","All","hello world").then((result) => console.log(result)) -``` - -This method returns a struct with the following fields: - -```typescript - interface Hashes { - MD5: string; - SHA1: string; - SHA256: string; - } -``` - -A TypeScript definition file is provided for this interface. - -## Support - -If you find a bug in this plugin, please raise a ticket [here](https://github.com/plugin/repository). -Please do not contact the Wails team for support. \ No newline at end of file diff --git a/v3/internal/plugins/template/models.d.ts.tmpl b/v3/internal/plugins/template/models.d.ts.tmpl deleted file mode 100644 index 567c91d70..000000000 --- a/v3/internal/plugins/template/models.d.ts.tmpl +++ /dev/null @@ -1,10 +0,0 @@ -// models.d.ts -// This file should contain any models that are used by the plugin. - -export namespace {{.Name}}Plugin { - export interface Hashes { - MD5: string; - SHA1: string; - SHA256: string; - } -} \ No newline at end of file diff --git a/v3/internal/plugins/template/plugin.go.tmpl b/v3/internal/plugins/template/plugin.go.tmpl deleted file mode 100644 index e8104f2bd..000000000 --- a/v3/internal/plugins/template/plugin.go.tmpl +++ /dev/null @@ -1,67 +0,0 @@ -package {{.Name}} - -import ( - "github.com/wailsapp/wails/v3/pkg/application" -) - -// ---------------- Plugin Setup ---------------- -// This is the main plugin struct. It can be named anything you like. -// It must implement the application.Plugin interface. -// Both the Init() and Shutdown() methods are called synchronously when the app starts and stops. - -type Config struct { - // Add any configuration options here -} - -type Plugin struct{ - config *Config - app *application.App -} - -func NewPlugin(config *Config) *Plugin { - return &Plugin{ - config: config, - } -} - -// Shutdown is called when the app is shutting down -// You can use this to clean up any resources you have allocated -func (p *Plugin) Shutdown() {} - -// Name returns the name of the plugin. -// You should use the go module format e.g. github.com/myuser/myplugin -func (p *Plugin) Name() string { - return "github.com/myuser/{{.Name}}" -} - -// Init is called when the app is starting up. You can use this to -// initialise any resources you need. You can also access the application -// instance via the app property. -func (p *Plugin) Init(app *application.App) error { - p.app = app - return nil -} - -// Exported returns a list of exported methods that can be called from the frontend -func (p *Plugin) CallableByJS() []string { - return []string{ - "Greet", - } -} - -// InjectJS returns any JS that should be injected into the frontend -func (p *Plugin) InjectJS() string { - return "" -} - -// ---------------- Plugin Methods ---------------- -// Plugin methods are just normal Go methods. You can add as many as you like. -// The only requirement is that they are exported (start with a capital letter). -// You can also return any type that is JSON serializable. -// Any methods that you want to be callable from the frontend must be returned by the -// CallableByJS() method above. -// See https://golang.org/pkg/encoding/json/#Marshal for more information. - -func (p *Plugin) Greet(name string) string { - return "Hello " + name -} diff --git a/v3/internal/plugins/template/plugin.tmpl.js b/v3/internal/plugins/template/plugin.tmpl.js deleted file mode 100644 index c62bbb6e7..000000000 --- a/v3/internal/plugins/template/plugin.tmpl.js +++ /dev/null @@ -1,46 +0,0 @@ -// plugin.js -// This file should contain helper functions for the that can be used by the frontend. -// Below are examples of how to use JSDoc to define the Hashes struct and the exported functions. - -/** - * @typedef {Object} Hashes - A collection of hashes. - * @property {string} md5 - The MD5 hash of a string, represented as a hexadecimal string. - * @property {string} sha1 - The SHA-1 hash of a string, represented as a hexadecimal string. - * @property {string} sha256 - The SHA-256 hash of a string, represented as a hexadecimal string. - */ - -/** - * Generate all hashes for a string. - * @param input {string} - The string to generate hashes for. - * @returns {Promise} - */ -export function All(input) { - return wails.Plugin("{{.Name}}", "All", input); -} - -/** - * Generate the MD5 hash for a string. - * @param input {string} - The string to generate the hash for. - * @returns {Promise} - */ -export function MD5(input) { - return wails.Plugin("{{.Name}}", "MD5", input); -} - -/** - * Generate the SHA-1 hash for a string. - * @param input {string} - The string to generate the hash for. - * @returns {Promise} - */ -export function SHA1(input) { - return wails.Plugin("{{.Name}}", "SHA1", input); -} - -/** - * Generate the SHA-256 hash for a string. - * @param input {string} - The string to generate the hash for. - * @returns {Promise} - */ -export function SHA256(input) { - return wails.Plugin("{{.Name}}", "SHA256", input); -} \ No newline at end of file diff --git a/v3/internal/plugins/template/plugin.tmpl.toml b/v3/internal/plugins/template/plugin.tmpl.toml deleted file mode 100644 index 76e7aa384..000000000 --- a/v3/internal/plugins/template/plugin.tmpl.toml +++ /dev/null @@ -1,11 +0,0 @@ -# This is the plugin definition file for the "{{.Name}}" plugin. - -Name = "{{.Name}}" -Description = "{{.Description}}" -Author = "" -Version = "" -Website = "" -Repository = "" -License = "" - - diff --git a/v3/internal/plugins/plugins.go b/v3/internal/service/service.go similarity index 64% rename from v3/internal/plugins/plugins.go rename to v3/internal/service/service.go index 05de5f908..76c1c2560 100644 --- a/v3/internal/plugins/plugins.go +++ b/v3/internal/service/service.go @@ -1,4 +1,4 @@ -package plugins +package service import ( "embed" @@ -15,19 +15,19 @@ import ( ) //go:embed template -var pluginTemplate embed.FS +var serviceTemplate embed.FS type TemplateOptions struct { - *flags.PluginInit + *flags.ServiceInit } -func Install(options *flags.PluginInit) error { +func Install(options *flags.ServiceInit) error { if options.OutputDir == "." || options.OutputDir == "" { options.OutputDir = filepath.Join(lo.Must(os.Getwd()), options.Name) } - fmt.Printf("Creating plugin '%s' into '%s'\n", options.Name, options.OutputDir) - tfs, err := fs.Sub(pluginTemplate, "template") + fmt.Printf("Generating service '%s' into '%s'\n", options.Name, options.OutputDir) + tfs, err := fs.Sub(serviceTemplate, "template") if err != nil { return err } diff --git a/v3/internal/service/template/README.tmpl.md b/v3/internal/service/template/README.tmpl.md new file mode 100644 index 000000000..bffd2a697 --- /dev/null +++ b/v3/internal/service/template/README.tmpl.md @@ -0,0 +1,129 @@ +# Wails v3 Service Template + +This README provides an overview of the Wails v3 service template and explains how to adapt it to create your own custom service. + +## Overview + +The service template provides a basic structure for creating a Wails v3 service. A service in Wails v3 is a Go package that can be integrated into your Wails application to provide specific functionality, handle HTTP requests, and interact with the frontend. + +## Template Structure + +The template defines a `MyService` struct and several methods: + +### MyService Struct + +```go +type MyService struct { + ctx context.Context + options application.ServiceOptions +} +``` + +This is the main service struct. You can rename it to better reflect your service's purpose. The struct holds a context and service options, which are set during startup. + +### Name Method + +```go +func (p *MyService) Name() string +``` + +This method returns the name of the service. It's used to identify the service within the Wails application. + +### OnStartup Method + +```go +func (p *MyService) OnStartup(ctx context.Context, options application.ServiceOptions) error +``` + +This method is called when the app is starting up. Use it to initialize resources, set up connections, or perform any necessary setup tasks. +It receives a context and service options, which are stored in the service struct. + +### OnShutdown Method + +```go +func (p *MyService) OnShutdown() error +``` + +This method is called when the app is shutting down. Use it to clean up resources, close connections, or perform any necessary cleanup tasks. + +### ServeHTTP Method + +```go +func (p *MyService) ServeHTTP(w http.ResponseWriter, r *http.Request) +``` + +This method handles HTTP requests to the service. It's called when the frontend makes an HTTP request to the backend +at the path specified in the `Route` field of the service options. + +### Service Methods + +```go +func (p *MyService) Greet(name string) string +``` + +This is an example of a service method. You can add as many methods as you need. These methods can be called from the frontend. + +## Adapting the Template + +To create your own service: + +1. Rename the `MyService` struct to reflect your service's purpose (e.g., `DatabaseService`, `AuthService`). +2. Update the `Name` method to return your service's unique identifier. +3. Implement the `OnStartup` method to initialize your service. This might include setting up database connections, loading configuration, etc. +4. If needed, implement the `OnShutdown` method to properly clean up resources when the application closes. +5. If your service needs to handle HTTP requests, implement the `ServeHTTP` method. Use this to create API endpoints, serve files, or handle any HTTP interactions. +6. Add your own methods to the service. These can include database operations, business logic, or any functionality your service needs to provide. +7. If your service requires configuration, consider adding a `Config` struct and a `New` function to create and configure your service. + +## Example: Database Service + +Here's how you might adapt the template for a database service: + +```go +type DatabaseService struct { + ctx context.Context + options application.ServiceOptions + db *sql.DB +} + +func (s *DatabaseService) Name() string { + return "github.com/myname/DatabaseService" +} + +func (s *DatabaseService) OnStartup(ctx context.Context, options application.ServiceOptions) error { + s.ctx = ctx + s.options = options + // Initialize database connection + var err error + s.db, err = sql.Open("mysql", "user:password@/dbname") + return err +} + +func (s *DatabaseService) OnShutdown() error { + return s.db.Close() +} + +func (s *DatabaseService) GetUser(id int) (User, error) { + // Implement database query +} + +// Add more methods as needed +``` + +## Long-running tasks + +If your service needs to perform long-running tasks, consider using goroutines and channels to manage these tasks. +You can use the `context.Context` to listen for when the application shuts down: + +```go +func (s *DatabaseService) longRunningTask() { + for { + select { + case <-s.ctx.Done(): + // Cleanup and exit + return + // Perform long-running task + } + } +} +``` diff --git a/v3/internal/plugins/template/go.mod.tmpl b/v3/internal/service/template/go.mod.tmpl similarity index 80% rename from v3/internal/plugins/template/go.mod.tmpl rename to v3/internal/service/template/go.mod.tmpl index 84ff2e07b..1b99c5892 100644 --- a/v3/internal/plugins/template/go.mod.tmpl +++ b/v3/internal/service/template/go.mod.tmpl @@ -1,8 +1,8 @@ module {{.Name}} -go 1.20 +go 1.23 -require github.com/wailsapp/wails/v3 v3.0.0-alpha.0 +require github.com/wailsapp/wails/v3 v3.0.0-alpha.4 require ( github.com/imdario/mergo v0.3.12 // indirect diff --git a/v3/internal/plugins/template/go.sum b/v3/internal/service/template/go.sum similarity index 100% rename from v3/internal/plugins/template/go.sum rename to v3/internal/service/template/go.sum diff --git a/v3/internal/service/template/service.go.tmpl b/v3/internal/service/template/service.go.tmpl new file mode 100644 index 000000000..9ba3990d9 --- /dev/null +++ b/v3/internal/service/template/service.go.tmpl @@ -0,0 +1,60 @@ +package {{.Name}} + +import ( + "context" + "github.com/wailsapp/wails/v3/pkg/application" +) + +// ---------------- Service Setup ---------------- +// This is the main service struct. It can be named anything you like. +// Both the OnStartup() and OnShutdown() methods are called synchronously when the app starts and stops. +// Changing the name of this struct will change the name of the services class in the frontend +// Bound methods will exist inside frontend/bindings/github.com/user/{{.Name}} under the name of the struct +type MyService struct{ + ctx context.Context + options application.ServiceOptions +} + +// Name is the name of the service +func (p *MyService) Name() string { + return "{{.Name}}" +} + +// OnStartup is called when the app is starting up. You can use this to +// initialise any resources you need. You can also access the application +// instance via the app property. +// OPTIONAL: This method is optional. +func (p *MyService) OnStartup(ctx context.Context, options application.ServiceOptions) error { + p.ctx = ctx + p.options = options + return nil +} + +// OnShutdown is called when the app is shutting down via runtime.Quit() call +// You can use this to clean up any resources you have allocated +// OPTIONAL: This method is optional. +func (p *MyService) OnShutdown() error { + return nil +} + +// ServeHTTP is called when the app is running and the frontend makes an HTTP request to the backend at the path +// specified in the `Route` field of the service Options. +// OPTIONAL: This method is optional. +func (p *MyService) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // You can use the request to get the path, query parameters, headers, etc. + // You can also use the response to set the status code, headers, body etc. + // Consult the net/http documentation for more information: https://pkg.go.dev/net/http + + // Log the request to the console + log.Printf("Received request: %s %s", r.Method, r.URL.Path) +} + +// ---------------- Service Methods ---------------- +// Service methods are just normal Go methods. You can add as many as you like. +// The only requirement is that they are exported (start with a capital letter). +// You can also return any type that is JSON serializable. +// See https://golang.org/pkg/encoding/json/#Marshal for more information. + +func (p *MyService) Greet(name string) string { + return "Hello " + name +} diff --git a/v3/internal/service/template/service.tmpl.yml b/v3/internal/service/template/service.tmpl.yml new file mode 100644 index 000000000..bd018461e --- /dev/null +++ b/v3/internal/service/template/service.tmpl.yml @@ -0,0 +1,8 @@ +# This is the plugin definition file for the "{{.Name}}" plugin. +Name: "{{.Name}}" +Description: "{{.Description}}" +Author: "{{.Author}}" +Version: "{{.Version}}" +Website: "{{.Website}}" +Repository: "{{.Repository}}" +License: "{{.License}}" diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index 540b749a9..de848f652 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -1,6 +1,7 @@ package application import ( + "context" "embed" "encoding/json" "fmt" @@ -135,18 +136,18 @@ func New(appOptions Options) *App { result.handleFatalError(fmt.Errorf("Fatal error in application initialisation: " + err.Error())) } - result.plugins = NewPluginManager(appOptions.Plugins, srv) - errors := result.plugins.Init() - if len(errors) > 0 { - for _, err := range errors { - result.handleError(fmt.Errorf("Error initialising plugin: " + err.Error())) + for _, service := range appOptions.Services { + if thisService, ok := service.instance.(ServiceStartup); ok { + err := thisService.OnStartup(result.ctx, service.options) + if err != nil { + name := service.options.Name + if name == "" { + name = getServiceName(service) + } + globalApplication.error("OnStartup() failed:", "service", name, "error", err.Error()) + continue + } } - result.handleFatalError(fmt.Errorf("fatal error in plugins initialisation")) - } - - err = result.bindings.AddPlugins(appOptions.Plugins) - if err != nil { - result.handleFatalError(fmt.Errorf("Fatal error in application initialisation: " + err.Error())) } // Process keybindings @@ -267,6 +268,8 @@ type eventHook struct { } type App struct { + ctx context.Context + cancel context.CancelFunc options Options applicationEventListeners map[uint][]*EventListener applicationEventListenersLock sync.RWMutex @@ -293,7 +296,6 @@ type App struct { pendingRun []runnable bindings *Bindings - plugins *PluginManager // platform app impl platformApp @@ -361,6 +363,7 @@ func (a *App) handleFatalError(err error) { } func (a *App) init() { + a.ctx, a.cancel = context.WithCancel(context.Background()) a.applicationEventHooks = make(map[uint][]*eventHook) a.applicationEventListeners = make(map[uint][]*EventListener) a.windows = make(map[uint]Window) @@ -440,6 +443,10 @@ func (a *App) RegisterListener(listener WailsEventListener) { a.wailsEventListenerLock.Unlock() } +func (a *App) RegisterServiceHandler(prefix string, handler http.Handler) { + a.assets.AttachServiceHandler(prefix, handler) +} + func (a *App) NewWebviewWindow() *WebviewWindow { return a.NewWebviewWindowWithOptions(WebviewWindowOptions{}) } @@ -579,10 +586,16 @@ func (a *App) Run() error { return err } - errors := a.plugins.Shutdown() - if len(errors) > 0 { - for _, err := range errors { - a.error("Error shutting down plugin: " + err.Error()) + // Cancel the context + a.cancel() + + for _, service := range a.options.Services { + // If it conforms to the ServiceShutdown interface, call the Shutdown method + if thisService, ok := service.instance.(ServiceShutdown); ok { + err := thisService.OnShutdown() + if err != nil { + a.error("Error shutting down service: " + err.Error()) + } } } diff --git a/v3/pkg/application/application_options.go b/v3/pkg/application/application_options.go index cfda959b2..b9a412567 100644 --- a/v3/pkg/application/application_options.go +++ b/v3/pkg/application/application_options.go @@ -13,12 +13,29 @@ import ( // Valid values may only be obtained by calling [NewService]. type Service struct { instance any + options ServiceOptions +} + +type ServiceOptions struct { + // Name can be set to override the name of the service + // This is useful for logging and debugging purposes + Name string + // Route is the path to the assets + Route string +} + +var DefaultServiceOptions = ServiceOptions{ + Route: "", } // NewService returns a Service value wrapping the given pointer. // If T is not a named type, the returned value is invalid. -func NewService[T any](instance *T) Service { - return Service{instance} +// The prefix is used if Service implements a http.Handler only one allowed +func NewService[T any](instance *T, options ...ServiceOptions) Service { + if len(options) == 1 { + return Service{instance, options[0]} + } + return Service{instance, DefaultServiceOptions} } func (s Service) Instance() any { @@ -62,9 +79,6 @@ type Options struct { // Assets are the application assets to be used. Assets AssetOptions - // Plugins is a map of plugins used by the application - Plugins map[string]Plugin - // Flags are key value pairs that are available to the frontend. // This is also used by Wails to provide information to the frontend. Flags map[string]any diff --git a/v3/pkg/application/bindings.go b/v3/pkg/application/bindings.go index 69ef3ed76..a7b1eb6c1 100644 --- a/v3/pkg/application/bindings.go +++ b/v3/pkg/application/bindings.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "reflect" "runtime" "strings" @@ -79,12 +80,17 @@ type Bindings struct { } func NewBindings(instances []Service, aliases map[uint32]uint32) (*Bindings, error) { + app := Get() b := &Bindings{ boundMethods: make(map[string]*BoundMethod), boundByID: make(map[uint32]*BoundMethod), methodAliases: aliases, } for _, binding := range instances { + handler, ok := binding.Instance().(http.Handler) + if ok && binding.options.Route != "" { + app.assets.AttachServiceHandler(binding.options.Route, handler) + } err := b.Add(binding.Instance()) if err != nil { return nil, err @@ -108,34 +114,6 @@ func (b *Bindings) Add(namedPtr interface{}) error { return nil } -func (b *Bindings) AddPlugins(plugins map[string]Plugin) error { - for pluginID, plugin := range plugins { - methods, err := b.getMethods(plugin, true) - if err != nil { - return fmt.Errorf("cannot add plugin '%s' to app: %s", pluginID, err.Error()) - } - - exportedMethods := plugin.CallableByJS() - - for _, method := range methods { - // Do not expose reserved methods - if lo.Contains(reservedPluginMethods, method.Name) { - continue - } - // Do not expose methods that are not in the exported list - if !lo.Contains(exportedMethods, method.Name) { - continue - } - - // Add it as a regular method - b.boundMethods[fmt.Sprintf("wails-plugins.%s.%s", pluginID, method.Name)] = method - b.boundByID[method.ID] = method - globalApplication.debug("Added plugin method: "+pluginID+"."+method.Name, "id", method.ID) - } - } - return nil -} - // Get returns the bound method with the given name func (b *Bindings) Get(options *CallOptions) *BoundMethod { method, ok := b.boundMethods[options.MethodName] diff --git a/v3/pkg/application/messageprocessor.go b/v3/pkg/application/messageprocessor.go index 3abc63d2a..62d7950dc 100644 --- a/v3/pkg/application/messageprocessor.go +++ b/v3/pkg/application/messageprocessor.go @@ -28,7 +28,6 @@ const ( ) type MessageProcessor struct { - pluginManager *PluginManager logger *slog.Logger runningCalls map[string]context.CancelFunc diff --git a/v3/pkg/application/plugins.go b/v3/pkg/application/plugins.go deleted file mode 100644 index 88bcbcb4f..000000000 --- a/v3/pkg/application/plugins.go +++ /dev/null @@ -1,67 +0,0 @@ -package application - -import ( - "github.com/pkg/errors" - "github.com/wailsapp/wails/v3/internal/assetserver" - "io/fs" -) - -type PluginAPI interface { -} - -type Plugin interface { - Name() string - Init(api PluginAPI) error - Shutdown() error - CallableByJS() []string - Assets() fs.FS -} - -type PluginManager struct { - plugins map[string]Plugin - assetServer *assetserver.AssetServer - initialisedPlugins []Plugin -} - -func NewPluginManager(plugins map[string]Plugin, assetServer *assetserver.AssetServer) *PluginManager { - result := &PluginManager{ - plugins: plugins, - assetServer: assetServer, - } - return result -} - -func (p *PluginManager) Init() []error { - - api := newPluginAPI() - for id, plugin := range p.plugins { - err := plugin.Init(api) - if err != nil { - globalApplication.error("Plugin '%s' failed to initialise: %s", plugin.Name(), err.Error()) - return p.Shutdown() - } - p.initialisedPlugins = append(p.initialisedPlugins, plugin) - assets := plugin.Assets() - if assets != nil { - err = p.assetServer.AddPluginAssets(id, assets) - if err != nil { - return []error{errors.Wrap(err, "Failed to add plugin assets: "+plugin.Name())} - } - } - globalApplication.debug("Plugin initialised: " + plugin.Name()) - } - return nil -} - -func (p *PluginManager) Shutdown() []error { - var errs []error - for _, plugin := range p.initialisedPlugins { - err := plugin.Shutdown() - globalApplication.debug("Plugin shutdown: " + plugin.Name()) - if err != nil { - err = errors.Wrap(err, "Plugin failed to shutdown: "+plugin.Name()) - errs = append(errs, err) - } - } - return errs -} diff --git a/v3/pkg/application/plugins_api.go b/v3/pkg/application/plugins_api.go deleted file mode 100644 index 36d235cb1..000000000 --- a/v3/pkg/application/plugins_api.go +++ /dev/null @@ -1,7 +0,0 @@ -package application - -type pluginAPI struct{} - -func newPluginAPI() *pluginAPI { - return &pluginAPI{} -} diff --git a/v3/pkg/application/services.go b/v3/pkg/application/services.go new file mode 100644 index 000000000..0df8cb055 --- /dev/null +++ b/v3/pkg/application/services.go @@ -0,0 +1,27 @@ +package application + +import ( + "context" + "reflect" +) + +type ServiceName interface { + Name() string +} + +type ServiceStartup interface { + OnStartup(ctx context.Context, options ServiceOptions) error +} + +type ServiceShutdown interface { + OnShutdown() error +} + +func getServiceName(service any) string { + // First check it conforms to ServiceName interface + if serviceName, ok := service.(ServiceName); ok { + return serviceName.Name() + } + // Next, get the name from the type + return reflect.TypeOf(service).String() +} diff --git a/v3/pkg/events/events.go b/v3/pkg/events/events.go index 3ef015872..bfa94d46a 100644 --- a/v3/pkg/events/events.go +++ b/v3/pkg/events/events.go @@ -1,29 +1,29 @@ package events type ApplicationEventType uint -type WindowEventType uint +type WindowEventType uint var Common = newCommonEvents() type commonEvents struct { ApplicationStarted ApplicationEventType - WindowMaximise WindowEventType - WindowUnMaximise WindowEventType - WindowFullscreen WindowEventType + WindowMaximise WindowEventType + WindowUnMaximise WindowEventType + WindowFullscreen WindowEventType WindowUnFullscreen WindowEventType - WindowRestore WindowEventType - WindowMinimise WindowEventType - WindowUnMinimise WindowEventType - WindowClosing WindowEventType - WindowZoom WindowEventType - WindowZoomIn WindowEventType - WindowZoomOut WindowEventType - WindowZoomReset WindowEventType - WindowFocus WindowEventType - WindowLostFocus WindowEventType - WindowShow WindowEventType - WindowHide WindowEventType - WindowDPIChanged WindowEventType + WindowRestore WindowEventType + WindowMinimise WindowEventType + WindowUnMinimise WindowEventType + WindowClosing WindowEventType + WindowZoom WindowEventType + WindowZoomIn WindowEventType + WindowZoomOut WindowEventType + WindowZoomReset WindowEventType + WindowFocus WindowEventType + WindowLostFocus WindowEventType + WindowShow WindowEventType + WindowHide WindowEventType + WindowDPIChanged WindowEventType WindowFilesDropped WindowEventType WindowRuntimeReady WindowEventType ThemeChanged ApplicationEventType @@ -88,130 +88,130 @@ func newLinuxEvents() linuxEvents { var Mac = newMacEvents() type macEvents struct { - ApplicationDidBecomeActive ApplicationEventType - ApplicationDidChangeBackingProperties ApplicationEventType - ApplicationDidChangeEffectiveAppearance ApplicationEventType - ApplicationDidChangeIcon ApplicationEventType - ApplicationDidChangeOcclusionState ApplicationEventType - ApplicationDidChangeScreenParameters ApplicationEventType - ApplicationDidChangeStatusBarFrame ApplicationEventType - ApplicationDidChangeStatusBarOrientation ApplicationEventType - ApplicationDidFinishLaunching ApplicationEventType - ApplicationDidHide ApplicationEventType - ApplicationDidResignActiveNotification ApplicationEventType - ApplicationDidUnhide ApplicationEventType - ApplicationDidUpdate ApplicationEventType - ApplicationWillBecomeActive ApplicationEventType - ApplicationWillFinishLaunching ApplicationEventType - ApplicationWillHide ApplicationEventType - ApplicationWillResignActive ApplicationEventType - ApplicationWillTerminate ApplicationEventType - ApplicationWillUnhide ApplicationEventType - ApplicationWillUpdate ApplicationEventType - ApplicationDidChangeTheme ApplicationEventType - ApplicationShouldHandleReopen ApplicationEventType - WindowDidBecomeKey WindowEventType - WindowDidBecomeMain WindowEventType - WindowDidBeginSheet WindowEventType - WindowDidChangeAlpha WindowEventType - WindowDidChangeBackingLocation WindowEventType - WindowDidChangeBackingProperties WindowEventType - WindowDidChangeCollectionBehavior WindowEventType - WindowDidChangeEffectiveAppearance WindowEventType - WindowDidChangeOcclusionState WindowEventType - WindowDidChangeOrderingMode WindowEventType - WindowDidChangeScreen WindowEventType - WindowDidChangeScreenParameters WindowEventType - WindowDidChangeScreenProfile WindowEventType - WindowDidChangeScreenSpace WindowEventType - WindowDidChangeScreenSpaceProperties WindowEventType - WindowDidChangeSharingType WindowEventType - WindowDidChangeSpace WindowEventType - WindowDidChangeSpaceOrderingMode WindowEventType - WindowDidChangeTitle WindowEventType - WindowDidChangeToolbar WindowEventType - WindowDidChangeVisibility WindowEventType - WindowDidDeminiaturize WindowEventType - WindowDidEndSheet WindowEventType - WindowDidEnterFullScreen WindowEventType - WindowDidEnterVersionBrowser WindowEventType - WindowDidExitFullScreen WindowEventType - WindowDidExitVersionBrowser WindowEventType - WindowDidExpose WindowEventType - WindowDidFocus WindowEventType - WindowDidMiniaturize WindowEventType - WindowDidMove WindowEventType - WindowDidOrderOffScreen WindowEventType - WindowDidOrderOnScreen WindowEventType - WindowDidResignKey WindowEventType - WindowDidResignMain WindowEventType - WindowDidResize WindowEventType - WindowDidUpdate WindowEventType - WindowDidUpdateAlpha WindowEventType - WindowDidUpdateCollectionBehavior WindowEventType - WindowDidUpdateCollectionProperties WindowEventType - WindowDidUpdateShadow WindowEventType - WindowDidUpdateTitle WindowEventType - WindowDidUpdateToolbar WindowEventType - WindowDidUpdateVisibility WindowEventType - WindowShouldClose WindowEventType - WindowWillBecomeKey WindowEventType - WindowWillBecomeMain WindowEventType - WindowWillBeginSheet WindowEventType - WindowWillChangeOrderingMode WindowEventType - WindowWillClose WindowEventType - WindowWillDeminiaturize WindowEventType - WindowWillEnterFullScreen WindowEventType - WindowWillEnterVersionBrowser WindowEventType - WindowWillExitFullScreen WindowEventType - WindowWillExitVersionBrowser WindowEventType - WindowWillFocus WindowEventType - WindowWillMiniaturize WindowEventType - WindowWillMove WindowEventType - WindowWillOrderOffScreen WindowEventType - WindowWillOrderOnScreen WindowEventType - WindowWillResignMain WindowEventType - WindowWillResize WindowEventType - WindowWillUnfocus WindowEventType - WindowWillUpdate WindowEventType - WindowWillUpdateAlpha WindowEventType - WindowWillUpdateCollectionBehavior WindowEventType - WindowWillUpdateCollectionProperties WindowEventType - WindowWillUpdateShadow WindowEventType - WindowWillUpdateTitle WindowEventType - WindowWillUpdateToolbar WindowEventType - WindowWillUpdateVisibility WindowEventType - WindowWillUseStandardFrame WindowEventType - MenuWillOpen ApplicationEventType - MenuDidOpen ApplicationEventType - MenuDidClose ApplicationEventType - MenuWillSendAction ApplicationEventType - MenuDidSendAction ApplicationEventType - MenuWillHighlightItem ApplicationEventType - MenuDidHighlightItem ApplicationEventType - MenuWillDisplayItem ApplicationEventType - MenuDidDisplayItem ApplicationEventType - MenuWillAddItem ApplicationEventType - MenuDidAddItem ApplicationEventType - MenuWillRemoveItem ApplicationEventType - MenuDidRemoveItem ApplicationEventType - MenuWillBeginTracking ApplicationEventType - MenuDidBeginTracking ApplicationEventType - MenuWillEndTracking ApplicationEventType - MenuDidEndTracking ApplicationEventType - MenuWillUpdate ApplicationEventType - MenuDidUpdate ApplicationEventType - MenuWillPopUp ApplicationEventType - MenuDidPopUp ApplicationEventType - MenuWillSendActionToItem ApplicationEventType - MenuDidSendActionToItem ApplicationEventType - WebViewDidStartProvisionalNavigation WindowEventType + ApplicationDidBecomeActive ApplicationEventType + ApplicationDidChangeBackingProperties ApplicationEventType + ApplicationDidChangeEffectiveAppearance ApplicationEventType + ApplicationDidChangeIcon ApplicationEventType + ApplicationDidChangeOcclusionState ApplicationEventType + ApplicationDidChangeScreenParameters ApplicationEventType + ApplicationDidChangeStatusBarFrame ApplicationEventType + ApplicationDidChangeStatusBarOrientation ApplicationEventType + ApplicationDidFinishLaunching ApplicationEventType + ApplicationDidHide ApplicationEventType + ApplicationDidResignActiveNotification ApplicationEventType + ApplicationDidUnhide ApplicationEventType + ApplicationDidUpdate ApplicationEventType + ApplicationWillBecomeActive ApplicationEventType + ApplicationWillFinishLaunching ApplicationEventType + ApplicationWillHide ApplicationEventType + ApplicationWillResignActive ApplicationEventType + ApplicationWillTerminate ApplicationEventType + ApplicationWillUnhide ApplicationEventType + ApplicationWillUpdate ApplicationEventType + ApplicationDidChangeTheme ApplicationEventType + ApplicationShouldHandleReopen ApplicationEventType + WindowDidBecomeKey WindowEventType + WindowDidBecomeMain WindowEventType + WindowDidBeginSheet WindowEventType + WindowDidChangeAlpha WindowEventType + WindowDidChangeBackingLocation WindowEventType + WindowDidChangeBackingProperties WindowEventType + WindowDidChangeCollectionBehavior WindowEventType + WindowDidChangeEffectiveAppearance WindowEventType + WindowDidChangeOcclusionState WindowEventType + WindowDidChangeOrderingMode WindowEventType + WindowDidChangeScreen WindowEventType + WindowDidChangeScreenParameters WindowEventType + WindowDidChangeScreenProfile WindowEventType + WindowDidChangeScreenSpace WindowEventType + WindowDidChangeScreenSpaceProperties WindowEventType + WindowDidChangeSharingType WindowEventType + WindowDidChangeSpace WindowEventType + WindowDidChangeSpaceOrderingMode WindowEventType + WindowDidChangeTitle WindowEventType + WindowDidChangeToolbar WindowEventType + WindowDidChangeVisibility WindowEventType + WindowDidDeminiaturize WindowEventType + WindowDidEndSheet WindowEventType + WindowDidEnterFullScreen WindowEventType + WindowDidEnterVersionBrowser WindowEventType + WindowDidExitFullScreen WindowEventType + WindowDidExitVersionBrowser WindowEventType + WindowDidExpose WindowEventType + WindowDidFocus WindowEventType + WindowDidMiniaturize WindowEventType + WindowDidMove WindowEventType + WindowDidOrderOffScreen WindowEventType + WindowDidOrderOnScreen WindowEventType + WindowDidResignKey WindowEventType + WindowDidResignMain WindowEventType + WindowDidResize WindowEventType + WindowDidUpdate WindowEventType + WindowDidUpdateAlpha WindowEventType + WindowDidUpdateCollectionBehavior WindowEventType + WindowDidUpdateCollectionProperties WindowEventType + WindowDidUpdateShadow WindowEventType + WindowDidUpdateTitle WindowEventType + WindowDidUpdateToolbar WindowEventType + WindowDidUpdateVisibility WindowEventType + WindowShouldClose WindowEventType + WindowWillBecomeKey WindowEventType + WindowWillBecomeMain WindowEventType + WindowWillBeginSheet WindowEventType + WindowWillChangeOrderingMode WindowEventType + WindowWillClose WindowEventType + WindowWillDeminiaturize WindowEventType + WindowWillEnterFullScreen WindowEventType + WindowWillEnterVersionBrowser WindowEventType + WindowWillExitFullScreen WindowEventType + WindowWillExitVersionBrowser WindowEventType + WindowWillFocus WindowEventType + WindowWillMiniaturize WindowEventType + WindowWillMove WindowEventType + WindowWillOrderOffScreen WindowEventType + WindowWillOrderOnScreen WindowEventType + WindowWillResignMain WindowEventType + WindowWillResize WindowEventType + WindowWillUnfocus WindowEventType + WindowWillUpdate WindowEventType + WindowWillUpdateAlpha WindowEventType + WindowWillUpdateCollectionBehavior WindowEventType + WindowWillUpdateCollectionProperties WindowEventType + WindowWillUpdateShadow WindowEventType + WindowWillUpdateTitle WindowEventType + WindowWillUpdateToolbar WindowEventType + WindowWillUpdateVisibility WindowEventType + WindowWillUseStandardFrame WindowEventType + MenuWillOpen ApplicationEventType + MenuDidOpen ApplicationEventType + MenuDidClose ApplicationEventType + MenuWillSendAction ApplicationEventType + MenuDidSendAction ApplicationEventType + MenuWillHighlightItem ApplicationEventType + MenuDidHighlightItem ApplicationEventType + MenuWillDisplayItem ApplicationEventType + MenuDidDisplayItem ApplicationEventType + MenuWillAddItem ApplicationEventType + MenuDidAddItem ApplicationEventType + MenuWillRemoveItem ApplicationEventType + MenuDidRemoveItem ApplicationEventType + MenuWillBeginTracking ApplicationEventType + MenuDidBeginTracking ApplicationEventType + MenuWillEndTracking ApplicationEventType + MenuDidEndTracking ApplicationEventType + MenuWillUpdate ApplicationEventType + MenuDidUpdate ApplicationEventType + MenuWillPopUp ApplicationEventType + MenuDidPopUp ApplicationEventType + MenuWillSendActionToItem ApplicationEventType + MenuDidSendActionToItem ApplicationEventType + WebViewDidStartProvisionalNavigation WindowEventType WebViewDidReceiveServerRedirectForProvisionalNavigation WindowEventType - WebViewDidFinishNavigation WindowEventType - WebViewDidCommitNavigation WindowEventType - WindowFileDraggingEntered WindowEventType - WindowFileDraggingPerformed WindowEventType - WindowFileDraggingExited WindowEventType + WebViewDidFinishNavigation WindowEventType + WebViewDidCommitNavigation WindowEventType + WindowFileDraggingEntered WindowEventType + WindowFileDraggingPerformed WindowEventType + WindowFileDraggingExited WindowEventType } func newMacEvents() macEvents { @@ -346,13 +346,13 @@ func newMacEvents() macEvents { var Windows = newWindowsEvents() type windowsEvents struct { - SystemThemeChanged ApplicationEventType - APMPowerStatusChange ApplicationEventType - APMSuspend ApplicationEventType - APMResumeAutomatic ApplicationEventType - APMResumeSuspend ApplicationEventType - APMPowerSettingChange ApplicationEventType - ApplicationStarted ApplicationEventType + SystemThemeChanged ApplicationEventType + APMPowerStatusChange ApplicationEventType + APMSuspend ApplicationEventType + APMResumeAutomatic ApplicationEventType + APMResumeSuspend ApplicationEventType + APMPowerSettingChange ApplicationEventType + ApplicationStarted ApplicationEventType WebViewNavigationCompleted WindowEventType WindowInactive WindowEventType WindowActive WindowEventType @@ -595,4 +595,3 @@ var eventToJS = map[uint]string{ 1204: "common:WindowDidMove", 1205: "common:WindowDidResize", } - diff --git a/v3/pkg/services/fileserver/fileserver.go b/v3/pkg/services/fileserver/fileserver.go new file mode 100644 index 000000000..ad9c11d60 --- /dev/null +++ b/v3/pkg/services/fileserver/fileserver.go @@ -0,0 +1,52 @@ +package fileserver + +import ( + "context" + "net/http" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +// ---------------- Service Setup ---------------- +// This is the main Service struct. It can be named anything you like. + +type Config struct { + RootPath string +} + +type Service struct { + config *Config + fs http.Handler +} + +func New(config *Config) *Service { + return &Service{ + config: config, + fs: http.FileServer(http.Dir(config.RootPath)), + } +} + +// OnShutdown is called when the app is shutting down +// You can use this to clean up any resources you have allocated +func (s *Service) OnShutdown() error { + return nil +} + +// Name returns the name of the plugin. +// You should use the go module format e.g. github.com/myuser/myplugin +func (s *Service) Name() string { + return "github.com/wailsapp/wails/v3/services/fileserver" +} + +// OnStartup is called when the app is starting up. You can use this to +// initialise any resources you need. +func (s *Service) OnStartup(ctx context.Context, options application.ServiceOptions) error { + // Any initialization code here + return nil +} + +func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Create a new file server rooted at the given path + // Strip the base path out of the request path + s.fs.ServeHTTP(w, r) +} diff --git a/v3/plugins/kvstore/kvstore.go b/v3/pkg/services/kvstore/kvstore.go similarity index 79% rename from v3/plugins/kvstore/kvstore.go rename to v3/pkg/services/kvstore/kvstore.go index dd87bc720..33f396a62 100644 --- a/v3/plugins/kvstore/kvstore.go +++ b/v3/pkg/services/kvstore/kvstore.go @@ -1,18 +1,15 @@ package kvstore import ( - "embed" + "context" "encoding/json" - "github.com/pkg/errors" - "github.com/wailsapp/wails/v3/pkg/application" "io" - "io/fs" "os" "sync" -) -//go:embed assets/* -var assets embed.FS + "github.com/pkg/errors" + "github.com/wailsapp/wails/v3/pkg/application" +) type KeyValueStore struct { config *Config @@ -27,17 +24,17 @@ type Config struct { AutoSave bool } -type Plugin struct{} +type Service struct{} -func NewPlugin(config *Config) *KeyValueStore { +func New(config *Config) *KeyValueStore { return &KeyValueStore{ config: config, data: make(map[string]any), } } -// Shutdown will save the store to disk if there are unsaved changes. -func (kvs *KeyValueStore) Shutdown() error { +// OnShutdown will save the store to disk if there are unsaved changes. +func (kvs *KeyValueStore) OnShutdown() error { if kvs.unsaved { err := kvs.Save() if err != nil { @@ -52,9 +49,8 @@ func (kvs *KeyValueStore) Name() string { return "github.com/wailsapp/wails/v3/plugins/kvstore" } -// Init is called when the plugin is loaded. It is passed the application.App -// instance. This is where you should do any setup. -func (kvs *KeyValueStore) Init(api application.PluginAPI) error { +// OnStartup is called when the plugin is loaded. This is where you should do any setup. +func (kvs *KeyValueStore) OnStartup(ctx context.Context, options application.ServiceOptions) error { err := kvs.open(kvs.config.Filename) if err != nil { return err @@ -63,21 +59,6 @@ func (kvs *KeyValueStore) Init(api application.PluginAPI) error { return nil } -func (kvs *KeyValueStore) CallableByJS() []string { - return []string{ - "Set", - "Get", - "Delete", - "Save", - } -} - -func (kvs *KeyValueStore) Assets() fs.FS { - return assets -} - -// ---------------- Plugin Methods ---------------- - func (kvs *KeyValueStore) open(filename string) (err error) { kvs.filename = filename kvs.data = make(map[string]any) diff --git a/v3/pkg/services/log/log.go b/v3/pkg/services/log/log.go new file mode 100644 index 000000000..64a2bcee1 --- /dev/null +++ b/v3/pkg/services/log/log.go @@ -0,0 +1,77 @@ +package log + +import ( + "context" + _ "embed" + "log/slog" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +type Config struct { + // Logger is the logger to use. If not set, a default logger will be used. + Logger *slog.Logger + + // LogLevel defines the log level of the logger. + LogLevel slog.Level + + // Handles errors that occur when writing to the log + ErrorHandler func(err error) +} + +type LoggerService struct { + config *Config + app *application.App + level slog.LevelVar +} + +func NewLoggerService(config *Config) *LoggerService { + if config.Logger == nil { + config.Logger = application.DefaultLogger(config.LogLevel) + } + + result := &LoggerService{ + config: config, + } + result.level.Set(config.LogLevel) + return result +} + +func New() *LoggerService { + return NewLoggerService(&Config{}) +} + +// OnShutdown is called when the app is shutting down +// You can use this to clean up any resources you have allocated +func (l *LoggerService) OnShutdown() error { return nil } + +// Name returns the name of the plugin. +// You should use the go module format e.g. github.com/myuser/myplugin +func (l *LoggerService) Name() string { + return "github.com/wailsapp/wails/v3/plugins/log" +} + +func (l *LoggerService) OnStartup(ctx context.Context, options application.ServiceOptions) error { + // Any initialization code here + return nil +} + +func (l *LoggerService) Debug(message string, args ...any) { + l.config.Logger.Debug(message, args...) +} + +func (l *LoggerService) Info(message string, args ...any) { + l.config.Logger.Info(message, args...) +} + +func (l *LoggerService) Warning(message string, args ...any) { + l.config.Logger.Warn(message, args...) +} + +func (l *LoggerService) Error(message string, args ...any) { + l.config.Logger.Error(message, args...) +} + +func (l *LoggerService) SetLogLevel(level slog.Level) { + l.level.Set(level) +} diff --git a/v3/pkg/services/sqlite/sqlite.go b/v3/pkg/services/sqlite/sqlite.go new file mode 100644 index 000000000..f96b345cd --- /dev/null +++ b/v3/pkg/services/sqlite/sqlite.go @@ -0,0 +1,127 @@ +package sqlite + +import ( + "context" + "database/sql" + "errors" + + "github.com/wailsapp/wails/v3/pkg/application" + _ "modernc.org/sqlite" +) + +// ---------------- Service Setup ---------------- +// This is the main Service struct. It can be named anything you like. + +type Config struct { + DBFile string +} + +type Service struct { + config *Config + conn *sql.DB +} + +func New(config *Config) *Service { + return &Service{ + config: config, + } +} + +// OnShutdown is called when the app is shutting down +// You can use this to clean up any resources you have allocated +func (s *Service) OnShutdown() error { + if s.conn != nil { + return s.conn.Close() + } + return nil +} + +// Name returns the name of the plugin. +// You should use the go module format e.g. github.com/myuser/myplugin +func (s *Service) Name() string { + return "github.com/wailsapp/wails/v3/plugins/sqlite" +} + +// OnStartup is called when the app is starting up. You can use this to +// initialise any resources you need. +func (s *Service) OnStartup(ctx context.Context, options application.ServiceOptions) error { + if s.config.DBFile == "" { + return errors.New(`no database file specified. Please set DBFile in the config to either a filename or use ":memory:" to use an in-memory database`) + } + db, err := s.Open(s.config.DBFile) + if err != nil { + return err + } + _ = db + + return nil +} + +func (s *Service) Open(dbPath string) (string, error) { + var err error + s.conn, err = sql.Open("sqlite", dbPath) + if err != nil { + return "", err + } + return "Database connection opened", nil +} + +func (s *Service) Execute(query string, args ...any) error { + if s.conn == nil { + return errors.New("no open database connection") + } + + _, err := s.conn.Exec(query, args...) + if err != nil { + return err + } + return nil +} + +func (s *Service) Select(query string, args ...any) ([]map[string]any, error) { + if s.conn == nil { + return nil, errors.New("no open database connection") + } + + rows, err := s.conn.Query(query, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + columns, err := rows.Columns() + var results []map[string]any + for rows.Next() { + values := make([]any, len(columns)) + pointers := make([]any, len(columns)) + + for i := range values { + pointers[i] = &values[i] + } + + if err := rows.Scan(pointers...); err != nil { + return nil, err + } + + row := make(map[string]any, len(columns)) + for i, column := range columns { + row[column] = values[i] + } + results = append(results, row) + } + + return results, nil +} + +func (s *Service) Close() (string, error) { + if s.conn == nil { + return "", errors.New("no open database connection") + } + + err := s.conn.Close() + if err != nil { + return "", err + } + s.conn = nil + return "Database connection closed", nil +} diff --git a/v3/plugins/kvstore/README.md b/v3/plugins/kvstore/README.md deleted file mode 100644 index c75c63435..000000000 --- a/v3/plugins/kvstore/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# KVStore Plugin - -This plugin provides a simple key/value store for your Wails applications. - -## Installation - -Add the plugin to the `Plugins` option in the Applications options: - -```go -package main - -import ( - "github.com/wailsapp/wails/v3/pkg/application" - "github.com/wailsapp/wails/v3/plugins/kvstore" -) - -func main() { - kvstorePlugin := kvstore.NewPlugin(&kvstore.Config{ - Filename: "myapp.db", - }) - app := application.New(application.Options{ - // ... - Plugins: map[string]application.Plugin{ - "kvstore": kvstorePlugin, - }, - }) - -``` - -### Options - -```go -type Config struct { - Filename string - AutoSave bool -} -``` - -- `Filename` - The name of the file to store the key/value pairs in. This file will be created in the application's data directory. -- `AutoSave` - If true, the store will be saved to disk after every change. If false, you will need to call `Save()` to persist the changes. - -## Usage - -### Go - -You can call the methods exported by the plugin directly: - -```go - // Set a key - err := kvstore.Set("url", "https://www.google.com") - if err != nil { - // handle error - } - // Get a key - url := kvstore.Get("url").(string) - - // Delete a key - err = kvstore.Delete("url") - if err != nil { - // handle error - } - - // If you have not enables AutoSave, you will need to call Save() to persist the changes - err = kvstore.Save() - if err != nil { - // handle error - } -``` - -### Javascript - -You can call the methods from the frontend using the Plugin method: - -```js - wails.Plugin("kvstore","Set", "url", "https://www.google.com") - wails.Plugin("kvstore","Get", "url").then((url) => { - - }) - wails.Plugin("kvstore","Delete", "url").then((url) => { - - }) - - // or - wails.Plugin("browser","OpenFile","/path/to/file") -``` - -## Support - -If you find a bug in this plugin, please raise a ticket on the Wails [Issue Tracker](https://github.com/wailsapp/wails/issues). diff --git a/v3/plugins/kvstore/assets/kvstore.js b/v3/plugins/kvstore/assets/kvstore.js deleted file mode 100644 index 13265677a..000000000 --- a/v3/plugins/kvstore/assets/kvstore.js +++ /dev/null @@ -1,42 +0,0 @@ -// plugin.js -// This file should contain helper functions for the that can be used by the frontend. -// Below are examples of how to use JSDoc to define the Hashes struct and the exported functions. - -import { Call } from '/wails/runtime.js'; - -/** - * Get the value of a key. - * @param key {string} - The store key. - * @returns {Promise} - The value of the key. - */ -export function Get(key) { - return Call.ByID(3322496224, key); -} - -/** - * Set the value of a key. - @param key {string} - The store key. - @param value {any} - The value to set. - * @returns {Promise} - */ -export function Set(key, value) { - return Call.ByID(1207638860, key, value); -} - - -/** - * Save the database to disk. - * @returns {Promise} - */ -export function Save() { - return Call.ByID(1377075201); -} - -/** - * Delete a key from the store. - * @param key {string} - The key to delete. - * @returns {Promise} - */ -export function Delete(key) { - return Call.ByID(737249231, key); -} \ No newline at end of file diff --git a/v3/plugins/kvstore/plugin.yaml b/v3/plugins/kvstore/plugin.yaml deleted file mode 100644 index 2c2a80cc2..000000000 --- a/v3/plugins/kvstore/plugin.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# This is the plugin definition file for the "kvstore" plugin. ---- -Name: kvstore -Description: A Simple Key/Value Store for Wails Applications -Author: Lea Anthony -Version: v1.0.0 -Website: https://wails.io -Repository: https://github.com/wailsapp/wails/v3/plugins/kvstore -License: MIT - - diff --git a/v3/plugins/log/README.md b/v3/plugins/log/README.md deleted file mode 100644 index cd5f511e1..000000000 --- a/v3/plugins/log/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# log Plugin - -This example plugin provides a way to generate hashes of strings. - -## Installation - -Add the plugin to the `Plugins` option in the Applications options: - -```go - Plugins: map[string]application.Plugin{ - "log": log.NewPlugin(), - }, -``` - -## Usage - -You can then call the methods from the frontend: - -```js - wails.Plugin("log","Debug","hello world") -``` - -### Methods - -- Trace -- Debug -- Info -- Warning -- Error -- Fatal -- SetLevel - -SetLevel takes an integer value from JS: - -```js - wails.Plugin("log","SetLevel",1) -``` - -Levels are: - - - Trace: 1 - - Debug: 2 - - Info: 3 - - Warning: 4 - - Error: 5 - - Fatal: 6 - -## Support - -If you find a bug in this plugin, please raise a ticket [here](https://github.com/plugin/repository). -Please do not contact the Wails team for support. \ No newline at end of file diff --git a/v3/plugins/log/assets/log.js b/v3/plugins/log/assets/log.js deleted file mode 100644 index 31afdc5ed..000000000 --- a/v3/plugins/log/assets/log.js +++ /dev/null @@ -1,61 +0,0 @@ -// plugin.js -// This file should contain helper functions for the that can be used by the frontend. -// Below are examples of how to use JSDoc to define the Hashes struct and the exported functions. - -import {Call} from '/wails/runtime.js'; - -/** - * Log at the Debug level. - * @param input {string} - The message in printf format. - * @param args {...any} - The arguments for the log message. - * @returns {Promise} - */ - -export function Debug(input, ...args) { - return Call.ByID(4111675027, input, ...args); -} - -/** - * Log at the Info level. - * @param input {string} - The message in printf format. - * @param args {...any} - The arguments for the log message. - * @returns {Promise} - */ -export function Info(input, ...args) { - return Call.ByID(2391172776, input, ...args); -} - -/** - * Log at the Warning level. - * @param input {string} - The message in printf format. - * @param args {...any} - The arguments for the log message. - * @returns {Promise} - */ -export function Warning(input, ...args) { - return Call.ByID(2762394760, input, ...args); -} - -/** - * Log at the Error level. - * @param input {string} - The message in printf format. - * @param args {...any} - The arguments for the log message. - * @returns {Promise} - */ -export function Error(input, ...args) { - return Call.ByID(878590242, input, ...args); -} - -export const LevelDebug = -4 -export const LevelInfo = 0 -export const LevelWarn = 4 -export const LevelError = 8 - - -/** - * Set Log level - * @param level {LogLevel} - The log level to set. - * @returns {Promise} - */ -export function SetLogLevel(level) { - return Call.ByID(2758810652, level); -} diff --git a/v3/plugins/log/plugin.go b/v3/plugins/log/plugin.go deleted file mode 100644 index b4cc9f64f..000000000 --- a/v3/plugins/log/plugin.go +++ /dev/null @@ -1,105 +0,0 @@ -package log - -import ( - "embed" - _ "embed" - "github.com/wailsapp/wails/v3/pkg/application" - "io/fs" - "log/slog" -) - -//go:embed assets/* -var assets embed.FS - -// ---------------- Plugin Setup ---------------- -// This is the main plugin struct. It can be named anything you like. -// It must implement the application.Plugin interface. -// Both the Init() and Shutdown() methods are called synchronously when the app starts and stops. - -type Config struct { - // Logger is the logger to use. If not set, a default logger will be used. - Logger *slog.Logger - - // LogLevel defines the log level of the logger. - LogLevel slog.Level - - // Handles errors that occur when writing to the log - ErrorHandler func(err error) -} - -type Plugin struct { - config *Config - app *application.App - level slog.LevelVar -} - -func NewPluginWithConfig(config *Config) *Plugin { - if config.Logger == nil { - config.Logger = application.DefaultLogger(config.LogLevel) - } - - result := &Plugin{ - config: config, - } - result.level.Set(config.LogLevel) - return result -} - -func NewPlugin() *Plugin { - return NewPluginWithConfig(&Config{}) -} - -// Shutdown is called when the app is shutting down -// You can use this to clean up any resources you have allocated -func (p *Plugin) Shutdown() error { return nil } - -// Name returns the name of the plugin. -// You should use the go module format e.g. github.com/myuser/myplugin -func (p *Plugin) Name() string { - return "github.com/wailsapp/wails/v3/plugins/log" -} - -func (p *Plugin) Init(api application.PluginAPI) error { - return nil -} - -// CallableByJS returns a list of methods that can be called from the frontend -func (p *Plugin) CallableByJS() []string { - return []string{ - "Debug", - "Info", - "Warning", - "Error", - "SetLogLevel", - } -} - -func (p *Plugin) Assets() fs.FS { - return assets -} - -// ---------------- Plugin Methods ---------------- -// Plugin methods are just normal Go methods. You can add as many as you like. -// The only requirement is that they are exported (start with a capital letter). -// You can also return any type that is JSON serializable. -// See https://golang.org/pkg/encoding/json/#Marshal for more information. - -func (p *Plugin) Debug(message string, args ...any) { - p.config.Logger.Debug(message, args...) -} - -func (p *Plugin) Info(message string, args ...any) { - p.config.Logger.Info(message, args...) -} - -func (p *Plugin) Warning(message string, args ...any) { - p.config.Logger.Warn(message, args...) -} - -func (p *Plugin) Error(message string, args ...any) { - p.config.Logger.Error(message, args...) -} - -func (p *Plugin) SetLogLevel(level slog.Level) { - p.level.Set(level) -} diff --git a/v3/plugins/log/plugin.yaml b/v3/plugins/log/plugin.yaml deleted file mode 100644 index ea0cf5f67..000000000 --- a/v3/plugins/log/plugin.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# This is the plugin definition file for the "log" plugin. ---- -Name: log -Description: A basic logger -Author: Lea Anthony -Version: v1.0.0 -Website: https://wails.io -Repository: https://github.com/wailsapp/wails/v3/plugins/log -License: MIT - - diff --git a/v3/plugins/sqlite/assets/sqlite.js b/v3/plugins/sqlite/assets/sqlite.js deleted file mode 100644 index 137a7591a..000000000 --- a/v3/plugins/sqlite/assets/sqlite.js +++ /dev/null @@ -1,39 +0,0 @@ - -import {Call} from '/wails/runtime.js'; - -/** - * Open a sqlite DB. - * @param filename {string} - file to open. - * @returns {Promise} - */ -export function Open(filename) { - return Call.ByID(147348976, filename); -} - -/** - * Close a sqlite DB. - * @returns {Promise} - */ -export function Close() { - return Call.ByID(3998329564); -} - -/** - * Execute a SQL statement. - * @param statement {string} - SQL statement to execute. - * @param args {...any} - Arguments to pass to the statement. - * @returns {Promise} - */ -export function Execute(statement, ...args) { - return Call.ByID(2804887383, statement, ...args); -} - -/** - * Perform a select query. - * @param statement {string} - Select SQL statement. - * @param args {...any} - Arguments to pass to the statement. - * @returns {Promise} - */ -export function Select(statement, ...args) { - return Call.ByID(2209315040, statement, ...args); -} diff --git a/v3/plugins/sqlite/plugin.go b/v3/plugins/sqlite/plugin.go deleted file mode 100644 index 94c3b682b..000000000 --- a/v3/plugins/sqlite/plugin.go +++ /dev/null @@ -1,160 +0,0 @@ -package sqlite - -import ( - "database/sql" - "embed" - _ "embed" - "errors" - "github.com/wailsapp/wails/v3/pkg/application" - "io/fs" - _ "modernc.org/sqlite" -) - -//go:embed assets/* -var assets embed.FS - -// ---------------- Plugin Setup ---------------- -// This is the main plugin struct. It can be named anything you like. -// It must implement the application.Plugin interface. -// Both the Init() and Shutdown() methods are called synchronously when the app starts and stops. - -type Config struct { - DBFile string - CanCallOpen bool - CanCallClose bool -} - -type Plugin struct { - config *Config - conn *sql.DB - callableMethods []string - js string -} - -func NewPlugin(config *Config) *Plugin { - return &Plugin{ - config: config, - } -} - -// Shutdown is called when the app is shutting down -// You can use this to clean up any resources you have allocated -func (p *Plugin) Shutdown() error { - if p.conn != nil { - return p.conn.Close() - } - return nil -} - -// Name returns the name of the plugin. -// You should use the go module format e.g. github.com/myuser/myplugin -func (p *Plugin) Name() string { - return "github.com/wailsapp/wails/v3/plugins/sqlite" -} - -// Init is called when the app is starting up. You can use this to -// initialise any resources you need. -func (p *Plugin) Init(api application.PluginAPI) error { - p.callableMethods = []string{"Execute", "Select"} - if p.config.CanCallOpen { - p.callableMethods = append(p.callableMethods, "Open") - } - if p.config.CanCallClose { - p.callableMethods = append(p.callableMethods, "Close") - } - if p.config.DBFile == "" { - return errors.New(`no database file specified. Please set DBFile in the config to either a filename or use ":memory:" to use an in-memory database`) - } - _, err := p.Open(p.config.DBFile) - if err != nil { - return err - } - - return nil -} - -// CallableByJS returns a list of exported methods that can be called from the frontend -func (p *Plugin) CallableByJS() []string { - return p.callableMethods -} - -func (p *Plugin) Assets() fs.FS { - return assets -} - -// ---------------- Plugin Methods ---------------- -// Plugin methods are just normal Go methods. You can add as many as you like. -// The only requirement is that they are exported (start with a capital letter). -// You can also return any type that is JSON serializable. -// Any methods that you want to be callable from the frontend must be returned by the -// Exported() method above. -// See https://golang.org/pkg/encoding/json/#Marshal for more information. - -func (p *Plugin) Open(dbPath string) (string, error) { - var err error - p.conn, err = sql.Open("sqlite", dbPath) - if err != nil { - return "", err - } - return "Database connection opened", nil -} - -func (p *Plugin) Execute(query string, args ...any) error { - if p.conn == nil { - return errors.New("no open database connection") - } - - _, err := p.conn.Exec(query, args...) - if err != nil { - return err - } - return nil -} - -func (p *Plugin) Select(query string, args ...any) ([]map[string]any, error) { - if p.conn == nil { - return nil, errors.New("no open database connection") - } - - rows, err := p.conn.Query(query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - columns, err := rows.Columns() - var results []map[string]any - for rows.Next() { - values := make([]any, len(columns)) - pointers := make([]any, len(columns)) - - for i := range values { - pointers[i] = &values[i] - } - - if err := rows.Scan(pointers...); err != nil { - return nil, err - } - - row := make(map[string]any, len(columns)) - for i, column := range columns { - row[column] = values[i] - } - results = append(results, row) - } - - return results, nil -} - -func (p *Plugin) Close() (string, error) { - if p.conn == nil { - return "", errors.New("no open database connection") - } - - err := p.conn.Close() - if err != nil { - return "", err - } - p.conn = nil - return "Database connection closed", nil -} diff --git a/v3/plugins/sqlite/plugin.yaml b/v3/plugins/sqlite/plugin.yaml deleted file mode 100644 index 08a3d6926..000000000 --- a/v3/plugins/sqlite/plugin.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# This is the plugin definition file for the "sqlite" plugin. ---- -Name: sqlite -Description: Provides easy access to SQLite DBs -Author: Lea Anthony -Version: v1.0.0 -Website: https://wails.io/plugins/sqlite -Repository: https://github.com/wailsapp/wails/v3/plugins/sqlite -License: MIT - -