Major plugin updates

This commit is contained in:
Lea Anthony
2024-04-14 21:41:33 +10:00
parent cf130a6e25
commit c7ed7e72d4
52 changed files with 1155 additions and 326 deletions
+2 -2
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
+202
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;
}
@@ -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).
@@ -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:
@@ -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 ----------------
+5 -7
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()
-8
View File
@@ -1,8 +0,0 @@
export namespace HashesPlugin {
export interface Hashes {
md5: string;
sha1: string;
sha256: string;
}
}
@@ -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);
}
+1 -1
View File
@@ -1 +1 @@
{"url2":"https://reddit.com"}
{"q":"www","url2":"https://reddit.com"}
Binary file not shown.
+1 -1
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
+61 -15
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
@@ -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;
}
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 endDrag() {
document.body.style.cursor = 'default';
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);
if (shouldDrag) {
shouldDrag = false;
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
if (mousePressed > 0) {
invoke("drag");
return;
}
}
}
function checkDrag(e) {
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
if(shouldDrag && mousePressed > 0) {
invoke("drag");
return false;
if (!resizable || !IsWindows()) {
return;
}
if (defaultCursor == null) {
defaultCursor = document.documentElement.style.cursor;
}
return shouldDrag;
}
function handleResize(e) {
let resizeHandleHeight = GetFlag("system.resizeHandleHeight") || 5;
let resizeHandleWidth = GetFlag("system.resizeHandleWidth") || 5;
@@ -122,4 +116,4 @@ function handleResize(e) {
else if (topBorder) setResize("n-resize");
else if (bottomBorder) setResize("s-resize");
else if (rightBorder) setResize("e-resize");
}
}
+9 -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())
}
+61
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 => ../..

Some files were not shown because too many files have changed in this diff Show More