5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-17 01:19:29 +08:00

Merge branch 'refs/heads/v3-alpha-feature/plugin-updates' into v3-alpha

This commit is contained in:
Lea Anthony 2024-04-14 21:42:16 +10:00
commit 6f41dbf954
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
52 changed files with 1157 additions and 328 deletions

View File

@ -0,0 +1,65 @@
# Wails v3 Plugin Guide
Wails v3 introduces the concept of plugins. A plugin is a self-contained module that can extend the functionality of your Wails application.
This guide will walk you through the structure and functionality of a Wails plugin.
## Plugin Structure
A Wails plugin is a standard Go module, typically consisting of the following files:
- `plugin.go`: This is the main Go file where the plugin's functionality is implemented.
- `plugin.yaml`: This is the plugin's metadata file. It contains information about the plugin such as its name, author, version, and more.
- `assets/`: This directory contains any static assets that the plugin might need.
- `README.md`: This file provides documentation for the plugin.
## Plugin Implementation
In `plugin.go`, a plugin is defined as a struct that implements the `application.Plugin` interface. This interface requires the following methods:
- `Init()`: This method is called when the plugin is initialized.
- `Shutdown()`: This method is called when the application is shutting down.
- `Name()`: This method returns the name of the plugin.
- `CallableByJS()`: This method returns a list of method names that can be called from the frontend.
In addition to these methods, you can define any number of additional methods that implement the plugin's functionality.
These methods can be called from the frontend using the `wails.Plugin()` function.
## Plugin Metadata
The `plugin.yaml` file contains metadata about the plugin. This includes the plugin's name, description, author, version, website, repository, and license.
## Plugin Assets
Any static assets that the plugin needs can be placed in the `assets/` directory.
These assets can be accessed by the frontend by requesting them at the plugin base path.
This path is `/wails/plugins/<plugin-name>/`.
### Example
If a plugin named `logopack` has an asset named `logo.png`, the frontend can access it at `/wails/plugins/logopack/logo.png`.
## Plugin Documentation
The `README.md` file provides documentation for the plugin. This should include instructions on how to install and use the plugin, as well as any other information that users of the plugin might find useful.
## Example
Here's an example of how to use a plugin in your Wails application:
```go
Plugins: map[string]application.Plugin{
"log": log.NewPlugin(),
},
```
And here's how you can call a plugin method from the frontend:
```js
wails.Plugin("log","Debug","hello world")
```
## Support
If you encounter any issues with a plugin, please raise a ticket in the plugin's repository.
!!! note
The Wails team does not provide support for third-party plugins.

View File

@ -141,6 +141,7 @@ nav:
- Next Steps: getting-started/next-steps.md
- Learn More:
- Runtime: learn/runtime.md
- Plugins: learn/plugins.md
- Feedback: getting-started/feedback.md
- Feedback: getting-started/feedback.md
- What's New in v3?: whats-new.md

View File

@ -24,10 +24,10 @@
</head>
<body>
<div class="container">
<div class="quarter" style="background-color: lightblue; --webkit-app-region: drag">Draggable</div>
<div class="quarter" style="background-color: lightblue; --wails-draggable: drag">Draggable</div>
<div class="quarter" style="background-color: lightgreen;">Not Draggable</div>
<div class="quarter" style="background-color: lightpink;">Not Draggable</div>
<div class="quarter" style="background-color: lightyellow; --webkit-app-region: drag">Draggable</div>
<div class="quarter" style="background-color: lightyellow; --wails-draggable: drag">Draggable</div>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,202 @@
html {
background-color: rgba(33, 37, 43);
text-align: center;
color: white;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-touch-callout: none;
}
body {
padding-top: 40px;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
overscroll-behavior: none;
}
.logo {
width: 30%;
height: 20%;
}
/* CSS */
.button-42 {
background-color: initial;
background-image: linear-gradient(-180deg, #555555, #2f2f2f);
border-radius: 6px;
box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px;
color: #FFFFFF;
cursor: pointer;
display: inline-block;
font-family: Inter,-apple-system,system-ui,Roboto,"Helvetica Neue",Arial,sans-serif;
height: 35px;
line-height: 35px;
outline: 0;
overflow: hidden;
padding: 0 20px;
pointer-events: auto;
position: relative;
text-align: center;
touch-action: manipulation;
user-select: none;
-webkit-user-select: none;
vertical-align: top;
white-space: nowrap;
z-index: 9;
border: 0;
transition: box-shadow .2s;
font-size: medium;
}
p {
font-size: 1rem;
}
.button-42:hover {
background-image: linear-gradient(-180deg, #cb3939, #610000);
}
html {
background-color: rgba(33, 37, 43);
text-align: center;
color: white;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-touch-callout: none;
}
body {
padding-top: 40px;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
overscroll-behavior: none;
}
.button-42 {
background-color: initial;
background-image: linear-gradient(-180deg, #555555, #2f2f2f);
border-radius: 6px;
box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px;
color: #FFFFFF;
cursor: pointer;
display: inline-block;
font-family: Inter,-apple-system,system-ui,Roboto,"Helvetica Neue",Arial,sans-serif;
height: 35px;
line-height: 35px;
outline: 0;
overflow: hidden;
padding: 0 20px;
pointer-events: auto;
position: relative;
text-align: center;
touch-action: manipulation;
user-select: none;
-webkit-user-select: none;
vertical-align: top;
white-space: nowrap;
z-index: 9;
border: 0;
transition: box-shadow .2s;
font-size: medium;
}
p {
font-size: 1rem;
}
.button-42:hover {
background-image: linear-gradient(-180deg, #cb3939, #610000);
}
.tab {
overflow: hidden;
background-color: #f1f1f100;
margin-left: 20px;
}
.tab button {
background-color: transparent; /* Make the background transparent */
color: white; /* Make the text white */
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
position: relative; /* Added for the underline */
}
.tab button::after { /* Added for the underline */
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background-color: #a40505;
visibility: hidden;
transition: all 0.3s ease-in-out;
}
.tab button:hover::after, /* Added for the underline */
.tab button.active::after { /* Added for the underline */
width: 100%;
visibility: visible;
}
.tab button.active {
background-color: transparent; /* Make the background transparent */
color: red;
}
.tabcontent {
display: none;
padding: 6px 12px;
border-top: none;
}
#sqlresults, #hashresults {
font-family: 'Courier New', Courier, monospace;
min-height: 100px;
}
#selectquery {
width: 90%;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #005467;
color: white;
}
tr:hover {
background-color: #888;
}
.error-message {
color: #FF4B2B; /* Bright red color for better visibility in dark mode */
background-color: #1E1E1E; /* Dark background for the error message */
border: 1px solid #FF4B2B; /* Border color same as text color */
padding: 10px; /* Padding around the text */
margin: 10px 0; /* Margin top and bottom */
border-radius: 5px; /* Rounded corners */
text-align: center; /* Center the text */
}
.narrowColumn {
width: 1%; /* Adjust as needed */
white-space: nowrap;
}

View File

@ -1,4 +0,0 @@
# Experimental Plugins
This directory contains experimental plugins. These plugins are not supported by the Wails team and are provided as-is.
They may be removed at any time. If you have any thoughts about these plugins, they can be discussed on the [Wails Discord](https://discord.gg/u8JkcWsG).

View File

@ -15,7 +15,8 @@ Add the plugin to the `Plugins` option in the Applications options:
You can then call the Generate method from the frontend:
```js
wails.Plugin("hashes","Generate","hello world").then((result) => console.log(result))
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:

View File

@ -6,6 +6,7 @@ import (
"crypto/sha256"
"encoding/hex"
"github.com/wailsapp/wails/v3/pkg/application"
"io/fs"
)
// ---------------- Plugin Setup ----------------
@ -32,8 +33,8 @@ func (r *Plugin) CallableByJS() []string {
}
}
func (r *Plugin) InjectJS() string {
return ""
func (r *Plugin) Assets() fs.FS {
return nil
}
// ---------------- Plugin Methods ----------------

View File

@ -2,8 +2,9 @@ package main
import (
"embed"
"log/slog"
"os"
"plugin_demo/plugins/hashes"
"plugin_demo/hashes"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/plugins/kvstore"
@ -24,6 +25,7 @@ func main() {
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
LogLevel: slog.LevelDebug,
Plugins: map[string]application.Plugin{
"hashes": hashes.NewPlugin(),
"log": log.NewPlugin(),
@ -34,10 +36,6 @@ func main() {
Filename: "store.json",
AutoSave: true,
}),
//"server": server.NewPlugin(&server.Config{
// Enabled: true,
// Port: 34115,
//}),
"single_instance": single_instance.NewPlugin(&single_instance.Config{
// When true, the original app will be activated when a second instance is launched
ActivateAppOnSubsequentLaunch: true,
@ -50,8 +48,8 @@ func main() {
})
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
DevToolsEnabled: true,
OpenInspectorOnStartup: true,
Width: 1024,
Height: 768,
})
err := app.Run()

View File

@ -1,8 +0,0 @@
export namespace HashesPlugin {
export interface Hashes {
md5: string;
sha1: string;
sha256: string;
}
}

View File

@ -1,4 +0,0 @@
// Generate takes a string and returns a number of hashes for it
export function Generate(input) {
return wails.Plugin("hashes","Generate",input);
}

View File

@ -1 +1 @@
{"url2":"https://reddit.com"}
{"q":"www","url2":"https://reddit.com"}

Binary file not shown.

View File

@ -1,6 +1,6 @@
module github.com/wailsapp/wails/v3
go 1.22
go 1.21
require (
github.com/atterpac/refresh v0.6.3

View File

@ -1,12 +1,14 @@
package assetserver
import (
"embed"
"fmt"
"html"
"io/fs"
"net"
"net/http"
"net/http/httptest"
"net/url"
"path"
"strings"
"time"
)
@ -14,6 +16,7 @@ import (
const (
webViewRequestHeaderWindowId = "x-wails-window-id"
webViewRequestHeaderWindowName = "x-wails-window-name"
pluginPrefix = "/wails/plugin"
)
type RuntimeHandler interface {
@ -25,13 +28,17 @@ type AssetServer struct {
handler http.Handler
pluginScripts map[string]string
//pluginScripts map[string]string
assetServerWebView
pluginAssets map[string]fs.FS
}
func NewAssetServer(options *Options) (*AssetServer, error) {
result := &AssetServer{options: options}
result := &AssetServer{
options: options,
pluginAssets: make(map[string]fs.FS),
}
userHandler := options.Handler
if userHandler == nil {
@ -84,8 +91,8 @@ func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userH
// header.Add(HeaderCacheControl, "no-cache")
//}
path := req.URL.Path
switch path {
reqPath := req.URL.Path
switch reqPath {
case "", "/", "/index.html":
recorder := httptest.NewRecorder()
userHandler.ServeHTTP(recorder, req)
@ -105,9 +112,34 @@ func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userH
}
default:
// Check if this is a plugin script
if script, ok := a.pluginScripts[path]; ok {
a.writeBlob(rw, path, []byte(script))
// 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)
return
}
a.writeBlob(rw, reqPath, file)
} else {
userHandler.ServeHTTP(rw, req)
}
@ -127,14 +159,28 @@ 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)
//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)
}
pluginName = strings.ReplaceAll(pluginName, "/", "_")
pluginName = html.EscapeString(pluginName)
pluginScriptName := fmt.Sprintf("/wails/plugin/%s.js", pluginName)
a.pluginScripts[pluginScriptName] = script
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) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,91 +7,85 @@
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 9 */
import {invoke, IsWindows} from "./system";
import {GetFlag} from "./flags";
// Setup
let shouldDrag = false;
let resizable = false;
let resizeEdge = null;
let defaultCursor = "auto";
window._wails = window._wails || {};
window._wails.setResizable = setResizable;
window._wails.endDrag = endDrag;
window._wails.setResizable = function(value) {
resizable = value;
};
window._wails.endDrag = function() {
document.body.style.cursor = 'default';
shouldDrag = false;
};
window.addEventListener('mousedown', onMouseDown);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
let shouldDrag = false;
let resizeEdge = null;
let resizable = false;
let defaultCursor = "auto";
function dragTest(e) {
let val = window.getComputedStyle(e.target).getPropertyValue("--webkit-app-region");
if (!val || val === "" || val.trim() !== "drag" || e.buttons !== 1) {
let val = window.getComputedStyle(e.target).getPropertyValue("--wails-draggable");
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
if (!val || val === "" || val.trim() !== "drag" || mousePressed === 0) {
return false;
}
return e.detail === 1;
}
function setResizable(value) {
resizable = value;
function onMouseDown(e) {
// Check for resizing
if (resizeEdge) {
invoke("resize:" + resizeEdge);
e.preventDefault();
return;
}
function endDrag() {
document.body.style.cursor = 'default';
if (dragTest(e)) {
// This checks for clicks on the scroll bar
if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) {
return;
}
shouldDrag = true;
} else {
shouldDrag = false;
}
}
function onMouseUp() {
shouldDrag = false;
}
function testResize() {
if( resizeEdge ) {
invoke(`resize:${resizeEdge}`);
return true
}
return false;
}
function onMouseDown(e) {
if(IsWindows() && testResize() || dragTest(e)) {
shouldDrag = !!isValidDrag(e);
}
}
function isValidDrag(e) {
// Ignore drag on scrollbars
return !(e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight);
}
function onMouseUp(e) {
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
if (mousePressed > 0) {
endDrag();
}
}
function setResize(cursor = defaultCursor) {
document.documentElement.style.cursor = cursor;
function setResize(cursor) {
document.documentElement.style.cursor = cursor || defaultCursor;
resizeEdge = cursor;
}
function onMouseMove(e) {
shouldDrag = checkDrag(e);
if (IsWindows() && resizable) {
handleResize(e);
}
}
function checkDrag(e) {
if (shouldDrag) {
shouldDrag = false;
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
if(shouldDrag && mousePressed > 0) {
if (mousePressed > 0) {
invoke("drag");
return false;
return;
}
return shouldDrag;
}
function handleResize(e) {
if (!resizable || !IsWindows()) {
return;
}
if (defaultCursor == null) {
defaultCursor = document.documentElement.style.cursor;
}
let resizeHandleHeight = GetFlag("system.resizeHandleHeight") || 5;
let resizeHandleWidth = GetFlag("system.resizeHandleWidth") || 5;

View File

@ -3,6 +3,7 @@ package application
import (
"github.com/pkg/errors"
"github.com/wailsapp/wails/v3/internal/assetserver"
"io/fs"
)
type PluginAPI interface {
@ -13,7 +14,7 @@ type Plugin interface {
Init(api PluginAPI) error
Shutdown() error
CallableByJS() []string
InjectJS() string
Assets() fs.FS
}
type PluginManager struct {
@ -33,16 +34,19 @@ func NewPluginManager(plugins map[string]Plugin, assetServer *assetserver.AssetS
func (p *PluginManager) Init() []error {
api := newPluginAPI()
for _, plugin := range p.plugins {
for id, plugin := range p.plugins {
err := plugin.Init(api)
if err != nil {
globalApplication.error("Plugin failed to initialise:", "plugin", plugin.Name(), "error", err.Error())
return p.Shutdown()
}
p.initialisedPlugins = append(p.initialisedPlugins, plugin)
injectJS := plugin.InjectJS()
if injectJS != "" {
p.assetServer.AddPluginScript(plugin.Name(), injectJS)
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())
}

View File

@ -0,0 +1,61 @@
module oauth
go 1.22
require (
github.com/gorilla/pat v1.0.2
github.com/gorilla/sessions v1.2.2
github.com/markbates/goth v1.79.0
github.com/wailsapp/wails/v3 v3.0.0-alpha.4
)
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/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/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/securecookie v1.1.2 // 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/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/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.22.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/oauth2 v0.17.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
replace github.com/wailsapp/wails/v3 => ../..

View File

@ -0,0 +1,219 @@
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/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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/pat v1.0.2 h1:TDh/RulbnPxMQACcwbgMF5Bf00jaGoeYBNu+XUFuwtE=
github.com/gorilla/pat v1.0.2/go.mod h1:ioQ7dFQ2KXmOmWLJs6vZAfRikcm2D2JyuLrL9b5wVCg=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
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/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/markbates/goth v1.79.0 h1:fUYi9R6VubVEK2bpmXvIUp7xRcxA68i8ovfUQx/i5Qc=
github.com/markbates/goth v1.79.0/go.mod h1:RBD+tcFnXul2NnYuODhnIweOcuVPkBohLfEvutPekcU=
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/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/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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
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/wailsapp/wails/v3 v3.0.0-alpha.4 h1:hDzzwi3LjDD8ucS++qmAWYZlfcPbKiVuSUFqXZQDGIY=
github.com/wailsapp/wails/v3 v3.0.0-alpha.4/go.mod h1:z2rXi3AEnMTnC4PDUJomQTVZ1yq5RCM+mgeLloDVhkA=
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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
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/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.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.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
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=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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=

View File

@ -2,6 +2,7 @@ package single_instance
import (
"fmt"
"io/fs"
"os"
"path/filepath"
@ -24,8 +25,8 @@ func (p *Plugin) CallableByJS() []string {
return []string{}
}
func (p *Plugin) InjectJS() string {
return ""
func (p *Plugin) Assets() fs.FS {
return nil
}
func NewPlugin(config *Config) *Plugin {

View File

@ -1,6 +1,9 @@
package start_at_login
import "github.com/wailsapp/wails/v3/pkg/application"
import (
"github.com/wailsapp/wails/v3/pkg/application"
"io/fs"
)
type Plugin struct {
disabled bool
@ -45,6 +48,6 @@ func (p *Plugin) CallableByJS() []string {
}
}
func (p *Plugin) InjectJS() string {
return ""
func (p *Plugin) Assets() fs.FS {
return nil
}

View File

@ -27,6 +27,18 @@ func main() {
```
### 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
@ -34,12 +46,20 @@ func main() {
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 {
@ -55,6 +75,9 @@ You can call the methods from the frontend using the Plugin method:
wails.Plugin("kvstore","Set", "url", "https://www.google.com")
wails.Plugin("kvstore","Get", "url").then((url) => {
})
wails.Plugin("kvstore","Delete", "url").then((url) => {
})
// or

View File

@ -2,13 +2,15 @@
// 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<any>} - The value of the key.
*/
export function Get(key) {
return wails.CallByID(3322496224, key);
return Call.ByID(3322496224, key);
}
/**
@ -18,7 +20,7 @@ export function Get(key) {
* @returns {Promise<void>}
*/
export function Set(key, value) {
return wails.CallByID(1207638860, key, value);
return Call.ByID(1207638860, key, value);
}
@ -27,5 +29,14 @@ export function Set(key, value) {
* @returns {Promise<void|Error>}
*/
export function Save() {
return wails.CallByID(1377075201);
return Call.ByID(1377075201);
}
/**
* Delete a key from the store.
* @param key {string} - The key to delete.
* @returns {Promise<void>}
*/
export function Delete(key) {
return Call.ByID(737249231, key);
}

View File

@ -1,14 +1,19 @@
package kvstore
import (
"embed"
"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
type KeyValueStore struct {
config *Config
filename string
@ -17,10 +22,6 @@ type KeyValueStore struct {
lock sync.RWMutex
}
func (kvs *KeyValueStore) InjectJS() string {
return ""
}
type Config struct {
Filename string
AutoSave bool
@ -66,12 +67,13 @@ func (kvs *KeyValueStore) CallableByJS() []string {
return []string{
"Set",
"Get",
"Delete",
"Save",
}
}
func (p *Plugin) InjectJS() string {
return ""
func (kvs *KeyValueStore) Assets() fs.FS {
return assets
}
// ---------------- Plugin Methods ----------------
@ -146,11 +148,24 @@ func (kvs *KeyValueStore) Get(key string) any {
// Set sets the value for the given key. If AutoSave is true, the store is saved to disk.
func (kvs *KeyValueStore) Set(key string, value any) error {
kvs.lock.Lock()
if value == nil {
delete(kvs.data, key)
} else {
kvs.data[key] = value
}
kvs.lock.Unlock()
if kvs.config.AutoSave {
err := kvs.Save()
if err != nil {
return err
}
kvs.unsaved = false
} else {
kvs.unsaved = true
}
return nil
}
// Delete deletes the key from the store. If AutoSave is true, the store is saved to disk.
func (kvs *KeyValueStore) Delete(key string) error {
kvs.lock.Lock()
delete(kvs.data, key)
kvs.lock.Unlock()
if kvs.config.AutoSave {
err := kvs.Save()

View File

@ -2,6 +2,8 @@
// 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.
@ -9,8 +11,8 @@
* @returns {Promise<void|Error>}
*/
function Debug(input, ...args) {
return wails.CallByID(4111675027, input, ...args);
export function Debug(input, ...args) {
return Call.ByID(4111675027, input, ...args);
}
/**
@ -19,8 +21,8 @@ function Debug(input, ...args) {
* @param args {...any} - The arguments for the log message.
* @returns {Promise<void|Error>}
*/
function Info(input, ...args) {
return wails.CallByID(2391172776, input, ...args);
export function Info(input, ...args) {
return Call.ByID(2391172776, input, ...args);
}
/**
@ -29,8 +31,8 @@ function Info(input, ...args) {
* @param args {...any} - The arguments for the log message.
* @returns {Promise<void|Error>}
*/
function Warning(input, ...args) {
return wails.CallByID(2762394760, input, ...args);
export function Warning(input, ...args) {
return Call.ByID(2762394760, input, ...args);
}
/**
@ -39,14 +41,14 @@ function Warning(input, ...args) {
* @param args {...any} - The arguments for the log message.
* @returns {Promise<void|Error>}
*/
function Error(input, ...args) {
return wails.CallByID(878590242, input, ...args);
export function Error(input, ...args) {
return Call.ByID(878590242, input, ...args);
}
const LevelDebug = -4
const LevelInfo = 0
const LevelWarn = 4
const LevelError = 8
export const LevelDebug = -4
export const LevelInfo = 0
export const LevelWarn = 4
export const LevelError = 8
/**
@ -54,6 +56,6 @@ const LevelError = 8
* @param level {LogLevel} - The log level to set.
* @returns {Promise<void|Error>}
*/
function SetLogLevel(level) {
return wails.CallByID(2758810652, level);
export function SetLogLevel(level) {
return Call.ByID(2758810652, level);
}

View File

@ -1,13 +1,15 @@
package log
import (
"embed"
_ "embed"
"github.com/wailsapp/wails/v3/pkg/application"
"io/fs"
"log/slog"
)
//go:embed plugin.js
var pluginJS string
//go:embed assets/*
var assets embed.FS
// ---------------- Plugin Setup ----------------
// This is the main plugin struct. It can be named anything you like.
@ -72,8 +74,8 @@ func (p *Plugin) CallableByJS() []string {
}
}
func (p *Plugin) InjectJS() string {
return pluginJS
func (p *Plugin) Assets() fs.FS {
return assets
}
// ---------------- Plugin Methods ----------------

View File

@ -0,0 +1,39 @@
import {Call} from '/wails/runtime.js';
/**
* Open a sqlite DB.
* @param filename {string} - file to open.
* @returns {Promise<void>}
*/
export function Open(filename) {
return Call.ByID(147348976, filename);
}
/**
* Close a sqlite DB.
* @returns {Promise<void>}
*/
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<void>}
*/
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<any>}
*/
export function Select(statement, ...args) {
return Call.ByID(2209315040, statement, ...args);
}

View File

@ -2,22 +2,16 @@ package sqlite
import (
"database/sql"
"embed"
_ "embed"
"errors"
"fmt"
"github.com/wailsapp/wails/v3/pkg/application"
"io/fs"
_ "modernc.org/sqlite"
"strings"
)
//go:embed sqlite_close.js
var closejs string
//go:embed sqlite_open.js
var openjs string
//go:embed sqlite_execute_select.js
var executeselectjs string
//go:embed assets/*
var assets embed.FS
// ---------------- Plugin Setup ----------------
// This is the main plugin struct. It can be named anything you like.
@ -25,10 +19,9 @@ var executeselectjs string
// Both the Init() and Shutdown() methods are called synchronously when the app starts and stops.
type Config struct {
// Add any configuration options here
CanOpenFromJS bool
CanCloseFromJS bool
DBFile string
CanCallOpen bool
CanCallClose bool
}
type Plugin struct {
@ -63,14 +56,11 @@ func (p *Plugin) Name() string {
// initialise any resources you need.
func (p *Plugin) Init(api application.PluginAPI) error {
p.callableMethods = []string{"Execute", "Select"}
p.js = executeselectjs
if p.config.CanOpenFromJS {
if p.config.CanCallOpen {
p.callableMethods = append(p.callableMethods, "Open")
p.js += openjs
}
if p.config.CanCloseFromJS {
if p.config.CanCallClose {
p.callableMethods = append(p.callableMethods, "Close")
p.js += closejs
}
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`)
@ -80,7 +70,6 @@ func (p *Plugin) Init(api application.PluginAPI) error {
return err
}
p.js += fmt.Sprintf("\nwindow.sqlite = { %s };", strings.Join(p.callableMethods, ", "))
return nil
}
@ -89,8 +78,8 @@ func (p *Plugin) CallableByJS() []string {
return p.callableMethods
}
func (p *Plugin) InjectJS() string {
return p.js
func (p *Plugin) Assets() fs.FS {
return assets
}
// ---------------- Plugin Methods ----------------
@ -157,15 +146,15 @@ func (p *Plugin) Select(query string, args ...any) ([]map[string]any, error) {
return results, nil
}
func (p *Plugin) Close() error {
func (p *Plugin) Close() (string, error) {
if p.conn == nil {
return errors.New("no open database connection")
return "", errors.New("no open database connection")
}
err := p.conn.Close()
if err != nil {
return err
return "", err
}
p.conn = nil
return nil
return "Database connection closed", nil
}

View File

@ -1,8 +0,0 @@
/**
* Close a sqlite DB.
* @returns {Promise<void>}
*/
function Close() {
return wails.CallByID(3998329564);
}

View File

@ -1,20 +0,0 @@
/**
* Execute a SQL statement.
* @param statement {string} - SQL statement to execute.
@param args {...any} - Arguments to pass to the statement.
* @returns {Promise<void>}
*/
function Execute(statement, ...args) {
return wails.CallByID(2804887383, statement, ...args);
}
/**
* Perform a select query.
* @param statement {string} - Select SQL statement.
* @param args {...any} - Arguments to pass to the statement.
* @returns {Promise<any>}
*/
function Select(statement, ...args) {
return wails.CallByID(2209315040, statement, ...args);
}

View File

@ -1,8 +0,0 @@
/**
* Open a sqlite DB.
* @param filename {string} - file to open.
* @returns {Promise<void>}
*/
function Open(filename) {
return wails.CallByID(147348976, filename);
}