mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Merge branch 'chore/move_assetserver_to_pkg' into future/exp
This commit is contained in:
commit
71fb7fadff
973 changed files with 69218 additions and 825 deletions
|
|
@ -107,9 +107,11 @@ func buildApplication(f *flags.Build) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = gomod.SyncGoMod(logger, f.UpdateWailsVersionGoMod)
|
||||
if err != nil {
|
||||
return err
|
||||
if !f.NoSyncGoMod {
|
||||
err = gomod.SyncGoMod(logger, f.UpdateWailsVersionGoMod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Check platform
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ type BuildCommon struct {
|
|||
SkipFrontend bool `name:"s" description:"Skips building the frontend"`
|
||||
Verbosity int `name:"v" description:"Verbosity level (0 = quiet, 1 = normal, 2 = verbose)"`
|
||||
Tags string `description:"Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated"`
|
||||
NoSyncGoMod bool `description:"Don't sync go.mod"`
|
||||
}
|
||||
|
||||
func (c BuildCommon) Default() BuildCommon {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/flags"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/gomod"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/logutils"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
|
@ -22,7 +18,11 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||
"github.com/samber/lo"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/flags"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/gomod"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/logutils"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/buildtags"
|
||||
|
||||
"github.com/google/shlex"
|
||||
|
|
@ -75,7 +75,7 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error {
|
|||
cwd := lo.Must(os.Getwd())
|
||||
|
||||
// Update go.mod to use current wails version
|
||||
err := gomod.SyncGoMod(logger, true)
|
||||
err := gomod.SyncGoMod(logger, !f.NoSyncGoMod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -96,30 +96,8 @@ func Application(f *flags.Dev, logger *clilogger.CLILogger) error {
|
|||
|
||||
buildOptions.UserTags = userTags
|
||||
|
||||
err = build.CreateEmbedDirectories(cwd, buildOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
projectConfig := f.ProjectConfig()
|
||||
|
||||
if !buildOptions.SkipBindings {
|
||||
if f.Verbosity == build.VERBOSE {
|
||||
logutils.LogGreen("Generating Bindings...")
|
||||
}
|
||||
stdout, err := bindings.GenerateBindings(bindings.Options{
|
||||
Tags: buildOptions.UserTags,
|
||||
TsPrefix: projectConfig.Bindings.TsGeneration.Prefix,
|
||||
TsSuffix: projectConfig.Bindings.TsGeneration.Suffix,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.Verbosity == build.VERBOSE {
|
||||
logutils.LogGreen(stdout)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup signal handler
|
||||
quitChannel := make(chan os.Signal, 1)
|
||||
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@ package gomod
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/gomod"
|
||||
"github.com/wailsapp/wails/v2/internal/goversion"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"os"
|
||||
)
|
||||
|
||||
func SyncGoMod(logger *clilogger.CLILogger, updateWailsVersion bool) error {
|
||||
|
|
@ -32,12 +34,13 @@ func SyncGoMod(logger *clilogger.CLILogger, updateWailsVersion bool) error {
|
|||
LogGreen("Updated go.mod to use Go '%s'", goversion.MinRequirement)
|
||||
}
|
||||
|
||||
if outOfSync, err := gomod.GoModOutOfSync(gomodData, internal.Version); err != nil {
|
||||
internalVersion := strings.TrimSpace(internal.Version)
|
||||
if outOfSync, err := gomod.GoModOutOfSync(gomodData, internalVersion); err != nil {
|
||||
return err
|
||||
} else if outOfSync {
|
||||
if updateWailsVersion {
|
||||
LogGreen("Updating go.mod to use Wails '%s'", internal.Version)
|
||||
gomodData, err = gomod.UpdateGoModVersion(gomodData, internal.Version)
|
||||
LogGreen("Updating go.mod to use Wails '%s'", internalVersion)
|
||||
gomodData, err = gomod.UpdateGoModVersion(gomodData, internalVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
v2.2.0
|
||||
v2.3.1
|
||||
|
|
@ -12,8 +12,9 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/devserver"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/dispatcher"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package binding_test
|
||||
|
||||
type StructWithAnonymousSubMultiLevelStruct struct {
|
||||
Name string `json:"name"`
|
||||
Meta struct {
|
||||
Age int `json:"age"`
|
||||
More struct {
|
||||
Info string `json:"info"`
|
||||
MoreInMore struct {
|
||||
Demo string `json:"demo"`
|
||||
} `json:"more_in_more"`
|
||||
} `json:"more"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
func (s StructWithAnonymousSubMultiLevelStruct) Get() StructWithAnonymousSubMultiLevelStruct {
|
||||
return s
|
||||
}
|
||||
|
||||
var AnonymousSubStructMultiLevelTest = BindingTest{
|
||||
name: "StructWithAnonymousSubMultiLevelStruct",
|
||||
structs: []interface{}{
|
||||
&StructWithAnonymousSubMultiLevelStruct{},
|
||||
},
|
||||
exemptions: nil,
|
||||
shouldError: false,
|
||||
want: `
|
||||
export namespace binding_test {
|
||||
export class StructWithAnonymousSubMultiLevelStruct {
|
||||
name: string;
|
||||
// Go type: struct { Age int "json:\"age\""; More struct { Info string "json:\"info\""; MoreInMore struct { Demo string "json:\"demo\"" } "json:\"more_in_more\"" } "json:\"more\"" }
|
||||
meta: any;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new StructWithAnonymousSubMultiLevelStruct(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.name = source["name"];
|
||||
this.meta = this.convertValues(source["meta"], Object);
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package binding_test
|
||||
|
||||
type StructWithAnonymousSubStruct struct {
|
||||
Name string `json:"name"`
|
||||
Meta struct {
|
||||
Age int `json:"age"`
|
||||
} `json:"meta"`
|
||||
}
|
||||
|
||||
func (s StructWithAnonymousSubStruct) Get() StructWithAnonymousSubStruct {
|
||||
return s
|
||||
}
|
||||
|
||||
var AnonymousSubStructTest = BindingTest{
|
||||
name: "StructWithAnonymousSubStruct",
|
||||
structs: []interface{}{
|
||||
&StructWithAnonymousSubStruct{},
|
||||
},
|
||||
exemptions: nil,
|
||||
shouldError: false,
|
||||
want: `
|
||||
export namespace binding_test {
|
||||
export class StructWithAnonymousSubStruct {
|
||||
name: string;
|
||||
// Go type: struct { Age int "json:\"age\"" }
|
||||
meta: any;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new StructWithAnonymousSubStruct(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.name = source["name"];
|
||||
this.meta = this.convertValues(source["meta"], Object);
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
return a;
|
||||
}
|
||||
if (a.slice) {
|
||||
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||
} else if ("object" === typeof a) {
|
||||
if (asMap) {
|
||||
for (const key of Object.keys(a)) {
|
||||
a[key] = new classs(a[key]);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
return new classs(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ export namespace binding_test {
|
|||
}
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.empty = this.convertValues(source["empty"], null);
|
||||
this.empty = this.convertValues(source["empty"], Object);
|
||||
}
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
if (!a) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package binding_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
const expectedPromiseBindings = `// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
export function ErrorReturn(arg1:number):Promise<void>;
|
||||
|
||||
export function NoReturn(arg1:string):Promise<void>;
|
||||
|
||||
export function SingleReturn(arg1:any):Promise<number>;
|
||||
|
||||
export function SingleReturnWithError(arg1:number):Promise<string>;
|
||||
|
||||
export function TwoReturn(arg1:any):Promise<string|number>;
|
||||
`
|
||||
|
||||
type PromisesTest struct{}
|
||||
|
||||
func (h *PromisesTest) NoReturn(_ string) {}
|
||||
func (h *PromisesTest) ErrorReturn(_ int) error { return nil }
|
||||
func (h *PromisesTest) SingleReturn(_ interface{}) int { return 0 }
|
||||
func (h *PromisesTest) SingleReturnWithError(_ int) (string, error) { return "", nil }
|
||||
func (h *PromisesTest) TwoReturn(_ interface{}) (string, int) { return "", 0 }
|
||||
|
||||
func TestPromises(t *testing.T) {
|
||||
// given
|
||||
generationDir := t.TempDir()
|
||||
|
||||
// setup
|
||||
testLogger := &logger.Logger{}
|
||||
b := binding.NewBindings(testLogger, []interface{}{&PromisesTest{}}, []interface{}{}, false)
|
||||
|
||||
// then
|
||||
err := b.GenerateGoBindings(generationDir)
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate the Go bindings: %v", err)
|
||||
}
|
||||
|
||||
// then
|
||||
rawGeneratedBindings, err := fs.ReadFile(os.DirFS(generationDir), "binding_test/PromisesTest.d.ts")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read the generated bindings: %v", err)
|
||||
}
|
||||
|
||||
// then
|
||||
generatedBindings := string(rawGeneratedBindings)
|
||||
if generatedBindings != expectedPromiseBindings {
|
||||
t.Fatalf("the generated bindings does not match the expected ones.\nWanted:\n%s\n\nGot:\n%s", expectedPromiseBindings, generatedBindings)
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,8 @@ func TestBindings_GenerateModels(t *testing.T) {
|
|||
MultistructTest,
|
||||
EmptyStructTest,
|
||||
GeneratedJsEntityTest,
|
||||
AnonymousSubStructTest,
|
||||
AnonymousSubStructMultiLevelTest,
|
||||
}
|
||||
|
||||
testLogger := &logger.Logger{}
|
||||
|
|
|
|||
|
|
@ -77,21 +77,27 @@ func (b *Bindings) GenerateGoBindings(baseDir string) error {
|
|||
args.Add(arg + ":" + goTypeToTypescriptType(input.TypeName, &importNamespaces))
|
||||
}
|
||||
tsBody.WriteString(args.Join(",") + "):")
|
||||
returnType := "Promise"
|
||||
if methodDetails.OutputCount() > 0 {
|
||||
// now build Typescript return types
|
||||
// If there is no return value or only returning error, TS returns Promise<void>
|
||||
// If returning single value, TS returns Promise<type>
|
||||
// If returning single value or error, TS returns Promise<type>
|
||||
// If returning two values, TS returns Promise<type1|type2>
|
||||
// Otherwise, TS returns Promise<type1> (instead of throwing Go error?)
|
||||
var returnType string
|
||||
if methodDetails.OutputCount() == 0 {
|
||||
returnType = "Promise<void>"
|
||||
} else if methodDetails.OutputCount() == 1 && methodDetails.Outputs[0].TypeName == "error" {
|
||||
returnType = "Promise<void>"
|
||||
} else {
|
||||
outputTypeName := entityFullReturnType(methodDetails.Outputs[0].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
firstType := goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
returnType += "<" + firstType
|
||||
if methodDetails.OutputCount() == 2 {
|
||||
if methodDetails.Outputs[1].TypeName != "error" {
|
||||
outputTypeName = entityFullReturnType(methodDetails.Outputs[1].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
secondType := goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
returnType += "|" + secondType
|
||||
}
|
||||
returnType = "Promise<" + firstType
|
||||
if methodDetails.OutputCount() == 2 && methodDetails.Outputs[1].TypeName != "error" {
|
||||
outputTypeName = entityFullReturnType(methodDetails.Outputs[1].TypeName, b.tsPrefix, b.tsSuffix, &importNamespaces)
|
||||
secondType := goTypeToTypescriptType(outputTypeName, &importNamespaces)
|
||||
returnType += "|" + secondType
|
||||
}
|
||||
returnType += ">"
|
||||
} else {
|
||||
returnType = "Promise<void>"
|
||||
}
|
||||
tsBody.WriteString(returnType + ";\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,10 @@ const bool IsFullScreen(void *ctx);
|
|||
const bool IsMinimised(void *ctx);
|
||||
const bool IsMaximised(void *ctx);
|
||||
|
||||
void ProcessURLResponse(void *inctx, unsigned long long requestId, int statusCode, void *headersString, int headersStringLength, void* data, int datalength);
|
||||
void ProcessURLDidReceiveResponse(void *inctx, unsigned long long requestId, int statusCode, void *headersString, int headersStringLength);
|
||||
bool ProcessURLDidReceiveData(void *inctx, unsigned long long requestId, void* data, int datalength);
|
||||
void ProcessURLDidFinish(void *inctx, unsigned long long requestId);
|
||||
int ProcessURLRequestReadBodyStream(void *inctx, unsigned long long requestId, void *buf, int bufLen);
|
||||
|
||||
/* Dialogs */
|
||||
|
||||
|
|
|
|||
|
|
@ -52,12 +52,33 @@ WailsContext* Create(const char* title, int width, int height, int frameless, in
|
|||
return result;
|
||||
}
|
||||
|
||||
void ProcessURLResponse(void *inctx, unsigned long long requestId, int statusCode, void *headersString, int headersStringLength, void* data, int datalength) {
|
||||
void ProcessURLDidReceiveResponse(void *inctx, unsigned long long requestId, int statusCode, void *headersString, int headersStringLength) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
@autoreleasepool {
|
||||
NSData *nsHeadersJSON = [NSData dataWithBytes:headersString length:headersStringLength];
|
||||
[ctx processURLDidReceiveResponse:requestId :statusCode :nsHeadersJSON];
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcessURLDidReceiveData(void *inctx, unsigned long long requestId, void* data, int datalength) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
@autoreleasepool {
|
||||
NSData *nsdata = [NSData dataWithBytes:data length:datalength];
|
||||
[ctx processURLResponse:requestId :statusCode :nsHeadersJSON :nsdata];
|
||||
return [ctx processURLDidReceiveData:requestId :nsdata];
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessURLDidFinish(void *inctx, unsigned long long requestId) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
@autoreleasepool {
|
||||
[ctx processURLDidFinish:requestId];
|
||||
}
|
||||
}
|
||||
|
||||
int ProcessURLRequestReadBodyStream(void *inctx, unsigned long long requestId, void *buf, int bufLen) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
@autoreleasepool {
|
||||
return [ctx processURLRequestReadBodyStream:requestId :buf :bufLen];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
- (void) disableWindowConstraints;
|
||||
@end
|
||||
|
||||
@interface WailsContext : NSObject <WKURLSchemeHandler,WKScriptMessageHandler,WKNavigationDelegate>
|
||||
@interface WailsContext : NSObject <WKURLSchemeHandler,WKScriptMessageHandler,WKNavigationDelegate,WKUIDelegate>
|
||||
|
||||
@property (retain) WailsWindow* mainWindow;
|
||||
@property (retain) WKWebView* webview;
|
||||
|
|
@ -89,7 +89,10 @@
|
|||
- (void) SaveFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(NSString*)filters;
|
||||
|
||||
- (void) loadRequest:(NSString*)url;
|
||||
- (void) processURLResponse:(unsigned long long)requestId :(int)statusCode :(NSData *)headersString :(NSData*)data;
|
||||
- (void) processURLDidReceiveResponse:(unsigned long long)requestId :(int)statusCode :(NSData *)headersJSON;
|
||||
- (bool) processURLDidReceiveData:(unsigned long long)requestId :(NSData *)data;
|
||||
- (void) processURLDidFinish:(unsigned long long)requestId;
|
||||
- (int) processURLRequestReadBodyStream:(unsigned long long)requestId :(void *)buf :(int)bufLen;
|
||||
- (void) ExecJS:(NSString*)script;
|
||||
- (NSScreen*) getCurrentScreen;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#import "message.h"
|
||||
#import "Role.h"
|
||||
|
||||
typedef void (^schemeTaskCaller)(id<WKURLSchemeTask>);
|
||||
|
||||
@implementation WailsWindow
|
||||
|
||||
- (BOOL)canBecomeKeyWindow
|
||||
|
|
@ -107,7 +109,6 @@
|
|||
[self.mouseEvent release];
|
||||
[self.userContentController release];
|
||||
[self.urlRequests release];
|
||||
[self.urlRequestsLock release];
|
||||
[self.applicationMenu release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
|
@ -138,7 +139,6 @@
|
|||
|
||||
- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString*)appearance :(bool)windowIsTranslucent :(int)minWidth :(int)minHeight :(int)maxWidth :(int)maxHeight {
|
||||
self.urlRequestsId = 0;
|
||||
self.urlRequestsLock = [NSLock new];
|
||||
self.urlRequests = [NSMutableDictionary new];
|
||||
|
||||
NSWindowStyleMask styleMask = 0;
|
||||
|
|
@ -250,7 +250,8 @@
|
|||
}
|
||||
|
||||
[self.webview setNavigationDelegate:self];
|
||||
|
||||
self.webview.UIDelegate = self;
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"];
|
||||
|
||||
|
|
@ -407,37 +408,76 @@
|
|||
[self.webview evaluateJavaScript:script completionHandler:nil];
|
||||
}
|
||||
|
||||
- (void) processURLResponse:(unsigned long long)requestId :(int)statusCode :(NSData *)headersJSON :(NSData *)data {
|
||||
NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId];
|
||||
|
||||
[self.urlRequestsLock lock];
|
||||
id<WKURLSchemeTask> urlSchemeTask = self.urlRequests[key];
|
||||
[self.urlRequestsLock unlock];
|
||||
- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters
|
||||
initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * URLs))completionHandler {
|
||||
|
||||
@try {
|
||||
if (urlSchemeTask == nil) {
|
||||
return;
|
||||
}
|
||||
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||
openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
|
||||
if (@available(macOS 10.14, *)) {
|
||||
openPanel.canChooseDirectories = parameters.allowsDirectories;
|
||||
}
|
||||
|
||||
[openPanel
|
||||
beginSheetModalForWindow:webView.window
|
||||
completionHandler:^(NSInteger result) {
|
||||
if (result == NSModalResponseOK)
|
||||
completionHandler(openPanel.URLs);
|
||||
else
|
||||
completionHandler(nil);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) processURLDidReceiveResponse:(unsigned long long)requestId :(int)statusCode :(NSData *)headersJSON {
|
||||
[self processURLSchemeTaskCall:requestId :^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData: headersJSON options: NSJSONReadingMutableContainers error: nil];
|
||||
NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields] autorelease];
|
||||
|
||||
@try {
|
||||
[urlSchemeTask didReceiveResponse:response];
|
||||
[urlSchemeTask didReceiveData:data];
|
||||
[urlSchemeTask didFinish];
|
||||
} @catch (NSException *exception) {
|
||||
// This is very bad to detect a stopped schemeTask this should be implemented in a better way
|
||||
// See todo in stopURLSchemeTask...
|
||||
if (![exception.reason isEqualToString: @"This task has already been stopped"]) {
|
||||
@throw exception;
|
||||
[urlSchemeTask didReceiveResponse:response];
|
||||
}];
|
||||
}
|
||||
|
||||
- (bool) processURLDidReceiveData:(unsigned long long)requestId :(NSData *)data {
|
||||
return [self processURLSchemeTaskCall:requestId :^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
[urlSchemeTask didReceiveData:data];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) processURLDidFinish:(unsigned long long)requestId {
|
||||
[self processURLSchemeTaskCall:requestId :^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
[urlSchemeTask didFinish];
|
||||
}];
|
||||
|
||||
NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId];
|
||||
[self removeURLSchemeTask:key];
|
||||
}
|
||||
|
||||
- (int) processURLRequestReadBodyStream:(unsigned long long)requestId :(void *)buf :(int)bufLen {
|
||||
int res = 0;
|
||||
int *pRes = &res;
|
||||
|
||||
bool hasRequest = [self processURLSchemeTaskCall:requestId :^(id<WKURLSchemeTask> urlSchemeTask) {
|
||||
NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream;
|
||||
if (!stream) {
|
||||
*pRes = -3;
|
||||
} else {
|
||||
NSStreamStatus status = stream.streamStatus;
|
||||
if (status == NSStreamStatusAtEnd) {
|
||||
*pRes = 0;
|
||||
} else if (status != NSStreamStatusOpen) {
|
||||
*pRes = -4;
|
||||
} else if (!stream.hasBytesAvailable) {
|
||||
*pRes = 0;
|
||||
} else {
|
||||
*pRes = [stream read:buf maxLength:bufLen];
|
||||
}
|
||||
}
|
||||
} @finally {
|
||||
[self.urlRequestsLock lock];
|
||||
[self.urlRequests removeObjectForKey:key]; // This will release the urlSchemeTask which was retained from the dictionary
|
||||
[self.urlRequestsLock unlock];
|
||||
}];
|
||||
|
||||
if (!hasRequest) {
|
||||
res = -2;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
||||
|
|
@ -447,6 +487,7 @@
|
|||
const char *headerJSON = "";
|
||||
const void *body = nil;
|
||||
int bodyLen = 0;
|
||||
int hasBodyStream = 0;
|
||||
|
||||
NSData *headers = [NSJSONSerialization dataWithJSONObject: urlSchemeTask.request.allHTTPHeaderFields options:0 error: nil];
|
||||
if (headers) {
|
||||
|
|
@ -457,23 +498,85 @@
|
|||
if (urlSchemeTask.request.HTTPBody) {
|
||||
body = urlSchemeTask.request.HTTPBody.bytes;
|
||||
bodyLen = urlSchemeTask.request.HTTPBody.length;
|
||||
} else {
|
||||
// TODO handle HTTPBodyStream
|
||||
} else if (urlSchemeTask.request.HTTPBodyStream) {
|
||||
hasBodyStream = 1;
|
||||
[urlSchemeTask.request.HTTPBodyStream open];
|
||||
}
|
||||
|
||||
[self.urlRequestsLock lock];
|
||||
self.urlRequestsId++;
|
||||
unsigned long long requestId = self.urlRequestsId;
|
||||
NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId];
|
||||
self.urlRequests[key] = urlSchemeTask;
|
||||
[self.urlRequestsLock unlock];
|
||||
unsigned long long requestId;
|
||||
@synchronized(self.urlRequests) {
|
||||
self.urlRequestsId++;
|
||||
requestId = self.urlRequestsId;
|
||||
NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId];
|
||||
self.urlRequests[key] = urlSchemeTask;
|
||||
}
|
||||
|
||||
processURLRequest(self, requestId, url, method, headerJSON, body, bodyLen);
|
||||
processURLRequest(self, requestId, url, method, headerJSON, body, bodyLen, hasBodyStream);
|
||||
}
|
||||
|
||||
- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
||||
// TODO implement the stopping process here in a better way...
|
||||
// As soon as we introduce response body streaming we need to rewrite this nevertheless.
|
||||
NSArray<NSNumber*> *keys;
|
||||
@synchronized(self.urlRequests) {
|
||||
keys = [self.urlRequests allKeys];
|
||||
}
|
||||
|
||||
for (NSNumber *key in keys) {
|
||||
if (self.urlRequests[key] == urlSchemeTask) {
|
||||
[self removeURLSchemeTask:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) removeURLSchemeTask:(NSNumber *)urlSchemeTaskKey {
|
||||
id<WKURLSchemeTask> urlSchemeTask = nil;
|
||||
@synchronized(self.urlRequests) {
|
||||
urlSchemeTask = self.urlRequests[urlSchemeTaskKey];
|
||||
}
|
||||
|
||||
if (!urlSchemeTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream;
|
||||
if (stream) {
|
||||
[stream close];
|
||||
}
|
||||
|
||||
@synchronized(self.urlRequests) {
|
||||
[self.urlRequests removeObjectForKey:urlSchemeTaskKey];
|
||||
}
|
||||
}
|
||||
|
||||
- (bool)processURLSchemeTaskCall:(unsigned long long)requestId :(schemeTaskCaller)fn {
|
||||
NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId];
|
||||
|
||||
id<WKURLSchemeTask> urlSchemeTask;
|
||||
@synchronized(self.urlRequests) {
|
||||
urlSchemeTask = self.urlRequests[key];
|
||||
}
|
||||
|
||||
if (urlSchemeTask == nil) {
|
||||
// Stopped task, drop content...
|
||||
return false;
|
||||
}
|
||||
|
||||
@try {
|
||||
fn(urlSchemeTask);
|
||||
} @catch (NSException *exception) {
|
||||
[self removeURLSchemeTask:key];
|
||||
|
||||
// This is very bad to detect a stopped schemeTask this should be implemented in a better way
|
||||
// But it seems to be very tricky to not deadlock when keeping a lock curing executing fn()
|
||||
// It seems like those call switch the thread back to the main thread and then deadlocks when they reentrant want
|
||||
// to get the lock again to start another request or stop it.
|
||||
if ([exception.reason isEqualToString: @"This task has already been stopped"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@throw exception;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
||||
|
|
|
|||
|
|
@ -14,22 +14,20 @@ package darwin
|
|||
*/
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
|
@ -37,7 +35,7 @@ import (
|
|||
const startURL = "wails://wails/"
|
||||
|
||||
var messageBuffer = make(chan string, 100)
|
||||
var requestBuffer = make(chan *request, 100)
|
||||
var requestBuffer = make(chan *wkWebViewRequest, 100)
|
||||
var callbackBuffer = make(chan uint, 10)
|
||||
|
||||
type Frontend struct {
|
||||
|
|
@ -90,13 +88,17 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||
} else {
|
||||
appBindings.DB().UpdateObfuscatedCallMap()
|
||||
}
|
||||
assets, err := assetserver.NewAssetServerMainPage(ctx, bindings, appoptions)
|
||||
|
||||
assets, err := assetserver.NewAssetServerMainPage(bindings, appoptions, ctx.Value("assetdir") != nil, myLogger, runtime.RuntimeAssetsBundle)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result.assets = assets
|
||||
|
||||
go result.startRequestProcessor()
|
||||
// Start 10 processors to handle requests in parallel
|
||||
for i := 0; i < 10; i++ {
|
||||
go result.startRequestProcessor()
|
||||
}
|
||||
}
|
||||
|
||||
go result.startMessageProcessor()
|
||||
|
|
@ -342,8 +344,10 @@ func (f *Frontend) ExecJS(js string) {
|
|||
f.mainWindow.ExecJS(js)
|
||||
}
|
||||
|
||||
func (f *Frontend) processRequest(r *request) {
|
||||
rw := httptest.NewRecorder()
|
||||
func (f *Frontend) processRequest(r *wkWebViewRequest) {
|
||||
rw := &wkWebViewResponseWriter{r: r}
|
||||
defer rw.Close()
|
||||
|
||||
f.assets.ProcessHTTPRequest(
|
||||
r.url,
|
||||
rw,
|
||||
|
|
@ -363,28 +367,6 @@ func (f *Frontend) processRequest(r *request) {
|
|||
return req, nil
|
||||
},
|
||||
)
|
||||
|
||||
header := map[string]string{}
|
||||
for k := range rw.Header() {
|
||||
header[k] = rw.Header().Get(k)
|
||||
}
|
||||
headerData, _ := json.Marshal(header)
|
||||
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if _contents := rw.Body.Bytes(); _contents != nil {
|
||||
content = unsafe.Pointer(&_contents[0])
|
||||
contentLen = len(_contents)
|
||||
}
|
||||
|
||||
var headers unsafe.Pointer
|
||||
var headersLen int
|
||||
if len(headerData) != 0 {
|
||||
headers = unsafe.Pointer(&headerData[0])
|
||||
headersLen = len(headerData)
|
||||
}
|
||||
|
||||
C.ProcessURLResponse(r.ctx, r.id, C.int(rw.Code), headers, C.int(headersLen), content, C.int(contentLen))
|
||||
}
|
||||
|
||||
//func (f *Frontend) processSystemEvent(message string) {
|
||||
|
|
@ -403,64 +385,12 @@ func (f *Frontend) processRequest(r *request) {
|
|||
// }
|
||||
//}
|
||||
|
||||
type request struct {
|
||||
id C.ulonglong
|
||||
url string
|
||||
method string
|
||||
headers string
|
||||
body []byte
|
||||
|
||||
ctx unsafe.Pointer
|
||||
}
|
||||
|
||||
func (r *request) GetHttpRequest() (*http.Request, error) {
|
||||
var body io.Reader
|
||||
if len(r.body) != 0 {
|
||||
body = bytes.NewReader(r.body)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(r.method, r.url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.headers != "" {
|
||||
var h map[string]string
|
||||
if err := json.Unmarshal([]byte(r.headers), &h); err != nil {
|
||||
return nil, fmt.Errorf("Unable to unmarshal request headers: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range h {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
//export processMessage
|
||||
func processMessage(message *C.char) {
|
||||
goMessage := C.GoString(message)
|
||||
messageBuffer <- goMessage
|
||||
}
|
||||
|
||||
//export processURLRequest
|
||||
func processURLRequest(ctx unsafe.Pointer, requestId C.ulonglong, url *C.char, method *C.char, headers *C.char, body unsafe.Pointer, bodyLen C.int) {
|
||||
var goBody []byte
|
||||
if body != nil && bodyLen != 0 {
|
||||
goBody = C.GoBytes(body, bodyLen)
|
||||
}
|
||||
|
||||
requestBuffer <- &request{
|
||||
id: requestId,
|
||||
url: C.GoString(url),
|
||||
method: C.GoString(method),
|
||||
headers: C.GoString(headers),
|
||||
body: goBody,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
//export processCallback
|
||||
func processCallback(callbackID uint) {
|
||||
callbackBuffer <- callbackID
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ extern "C"
|
|||
#endif
|
||||
|
||||
void processMessage(const char *);
|
||||
void processURLRequest(void*, unsigned long long, const char *, const char *, const char *, const void *, int);
|
||||
void processURLRequest(void*, unsigned long long, const char *, const char *, const char *, const void *, int, int);
|
||||
void processMessageDialogResponse(int);
|
||||
void processOpenFileDialogResponse(const char*);
|
||||
void processSaveFileDialogResponse(const char*);
|
||||
|
|
|
|||
106
v2/internal/frontend/desktop/darwin/wkwebview_request.go
Normal file
106
v2/internal/frontend/desktop/darwin/wkwebview_request.go
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
//go:build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
|
||||
#import "Application.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//export processURLRequest
|
||||
func processURLRequest(ctx unsafe.Pointer, requestId C.ulonglong, url *C.char, method *C.char, headers *C.char, body unsafe.Pointer, bodyLen C.int, hasBodyStream C.int) {
|
||||
var bodyReader io.Reader
|
||||
if body != nil && bodyLen != 0 {
|
||||
bodyReader = bytes.NewReader(C.GoBytes(body, bodyLen))
|
||||
} else if hasBodyStream != 0 {
|
||||
bodyReader = &bodyStreamReader{id: requestId, ctx: ctx}
|
||||
}
|
||||
|
||||
requestBuffer <- &wkWebViewRequest{
|
||||
id: requestId,
|
||||
url: C.GoString(url),
|
||||
method: C.GoString(method),
|
||||
headers: C.GoString(headers),
|
||||
body: bodyReader,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
type wkWebViewRequest struct {
|
||||
id C.ulonglong
|
||||
url string
|
||||
method string
|
||||
headers string
|
||||
body io.Reader
|
||||
|
||||
ctx unsafe.Pointer
|
||||
}
|
||||
|
||||
func (r *wkWebViewRequest) GetHttpRequest() (*http.Request, error) {
|
||||
req, err := http.NewRequest(r.method, r.url, r.body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r.headers != "" {
|
||||
var h map[string]string
|
||||
if err := json.Unmarshal([]byte(r.headers), &h); err != nil {
|
||||
return nil, fmt.Errorf("unable to unmarshal request headers: %s", err)
|
||||
}
|
||||
|
||||
for k, v := range h {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
var _ io.Reader = &bodyStreamReader{}
|
||||
|
||||
type bodyStreamReader struct {
|
||||
id C.ulonglong
|
||||
ctx unsafe.Pointer
|
||||
}
|
||||
|
||||
// Read implements io.Reader
|
||||
func (r *bodyStreamReader) Read(p []byte) (n int, err error) {
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if p != nil {
|
||||
content = unsafe.Pointer(&p[0])
|
||||
contentLen = len(p)
|
||||
}
|
||||
|
||||
res := C.ProcessURLRequestReadBodyStream(r.ctx, r.id, content, C.int(contentLen))
|
||||
if res > 0 {
|
||||
return int(res), nil
|
||||
}
|
||||
|
||||
switch res {
|
||||
case 0:
|
||||
return 0, io.EOF
|
||||
case -1:
|
||||
return 0, fmt.Errorf("body: stream error")
|
||||
case -2:
|
||||
return 0, errRequestStopped
|
||||
case -3:
|
||||
return 0, fmt.Errorf("body: no stream defined")
|
||||
case -4:
|
||||
return 0, io.ErrClosedPipe
|
||||
default:
|
||||
return 0, fmt.Errorf("body: unknown error %d", res)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
//go:build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
|
||||
#import "Application.h"
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
errRequestStopped = errors.New("request has been stopped")
|
||||
)
|
||||
|
||||
type wkWebViewResponseWriter struct {
|
||||
r *wkWebViewRequest
|
||||
|
||||
header http.Header
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (rw *wkWebViewResponseWriter) Header() http.Header {
|
||||
if rw.header == nil {
|
||||
rw.header = http.Header{}
|
||||
}
|
||||
return rw.header
|
||||
}
|
||||
|
||||
func (rw *wkWebViewResponseWriter) Write(buf []byte) (int, error) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
|
||||
var content unsafe.Pointer
|
||||
var contentLen int
|
||||
if buf != nil {
|
||||
content = unsafe.Pointer(&buf[0])
|
||||
contentLen = len(buf)
|
||||
}
|
||||
|
||||
if !C.ProcessURLDidReceiveData(rw.r.ctx, rw.r.id, content, C.int(contentLen)) {
|
||||
return 0, errRequestStopped
|
||||
}
|
||||
return contentLen, nil
|
||||
}
|
||||
|
||||
func (rw *wkWebViewResponseWriter) WriteHeader(code int) {
|
||||
if rw.wroteHeader {
|
||||
return
|
||||
}
|
||||
rw.wroteHeader = true
|
||||
|
||||
header := map[string]string{}
|
||||
for k := range rw.Header() {
|
||||
header[k] = rw.Header().Get(k)
|
||||
}
|
||||
headerData, _ := json.Marshal(header)
|
||||
|
||||
var headers unsafe.Pointer
|
||||
var headersLen int
|
||||
if len(headerData) != 0 {
|
||||
headers = unsafe.Pointer(&headerData[0])
|
||||
headersLen = len(headerData)
|
||||
}
|
||||
|
||||
C.ProcessURLDidReceiveResponse(rw.r.ctx, rw.r.id, C.int(code), headers, C.int(headersLen))
|
||||
}
|
||||
|
||||
func (rw *wkWebViewResponseWriter) Close() {
|
||||
if !rw.wroteHeader {
|
||||
rw.WriteHeader(http.StatusNotImplemented)
|
||||
}
|
||||
C.ProcessURLDidFinish(rw.r.ctx, rw.r.id)
|
||||
}
|
||||
|
|
@ -88,9 +88,11 @@ import (
|
|||
"text/template"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
wailsruntime "github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
|
@ -159,7 +161,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||
} else {
|
||||
appBindings.DB().UpdateObfuscatedCallMap()
|
||||
}
|
||||
assets, err := assetserver.NewAssetServerMainPage(ctx, bindings, appoptions)
|
||||
assets, err := assetserver.NewAssetServerMainPage(bindings, appoptions, ctx.Value("assetdir") != nil, myLogger, wailsruntime.RuntimeAssetsBundle)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
)
|
||||
|
||||
const webkit2MinMinorVersion = 36
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import (
|
|||
"net/http"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
)
|
||||
|
||||
const webkit2MinMinorVersion = 0
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
)
|
||||
|
||||
type webKitResponseWriter struct {
|
||||
|
|
|
|||
|
|
@ -165,7 +165,6 @@ float xroot = 0.0f;
|
|||
float yroot = 0.0f;
|
||||
int dragTime = -1;
|
||||
uint mouseButton = 0;
|
||||
bool contextMenuDisabled = false;
|
||||
|
||||
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, void* dummy)
|
||||
{
|
||||
|
|
@ -175,8 +174,8 @@ gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, void* dummy)
|
|||
return FALSE;
|
||||
}
|
||||
mouseButton = event->button;
|
||||
if( event->button == 3 && contextMenuDisabled ) {
|
||||
return TRUE;
|
||||
if( event->button == 3 ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (event->type == GDK_BUTTON_PRESS && event->button == 1)
|
||||
|
|
@ -605,12 +604,15 @@ gboolean UnFullscreen(gpointer data) {
|
|||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
bool disableContextMenu(GtkWindow* window) {
|
||||
|
||||
// function to disable the context menu but propogate the event
|
||||
gboolean disableContextMenu(GtkWidget *widget, WebKitContextMenu *context_menu, GdkEvent *event, WebKitHitTestResult *hit_test_result, gpointer data) {
|
||||
// return true to disable the context menu
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void DisableContextMenu(void* webview) {
|
||||
contextMenuDisabled = TRUE;
|
||||
// Disable the context menu but propogate the event
|
||||
g_signal_connect(WEBKIT_WEB_VIEW(webview), "context-menu", G_CALLBACK(disableContextMenu), NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ import (
|
|||
"github.com/bep/debounce"
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/pkg/edge"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/win32"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||
wailsruntime "github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
)
|
||||
|
|
@ -102,7 +103,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
|||
appBindings.DB().UpdateObfuscatedCallMap()
|
||||
}
|
||||
|
||||
assets, err := assetserver.NewAssetServerMainPage(ctx, bindings, appoptions)
|
||||
assets, err := assetserver.NewAssetServerMainPage(bindings, appoptions, ctx.Value("assetdir") != nil, myLogger, wailsruntime.RuntimeAssetsBundle)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build windows && exp_gowebview2loader
|
||||
//go:build windows && !native_webview2loader
|
||||
|
||||
package edge
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build windows && !exp_gowebview2loader
|
||||
//go:build windows && native_webview2loader
|
||||
|
||||
package edge
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build windows && exp_gowebview2loader
|
||||
//go:build windows && !native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println("DEB | Using experimental go webview2loader")
|
||||
fmt.Println("DEB | Using go webview2loader")
|
||||
}
|
||||
|
||||
type webView2RunTimeType int32
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build exp_gowebview2loader
|
||||
//go:build windows && !native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build windows && exp_gowebview2loader
|
||||
//go:build windows && !native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build windows && exp_gowebview2loader
|
||||
//go:build windows && !native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build windows && !exp_gowebview2loader
|
||||
//go:build windows && native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !exp_gowebview2loader
|
||||
//go:build windows && native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !exp_gowebview2loader
|
||||
//go:build windows && native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !exp_gowebview2loader
|
||||
//go:build windows && native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//go:build exp_gowebview2loader
|
||||
//go:build windows && !native_webview2loader
|
||||
|
||||
package webviewloader
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
|
|
@ -61,6 +64,8 @@ func (d *DevWebServer) Run(ctx context.Context) error {
|
|||
|
||||
var assetHandler http.Handler
|
||||
var wsHandler http.Handler
|
||||
var myLogger *logger.Logger
|
||||
|
||||
_fronendDevServerURL, _ := ctx.Value("frontenddevserverurl").(string)
|
||||
if _fronendDevServerURL == "" {
|
||||
assetdir, _ := ctx.Value("assetdir").(string)
|
||||
|
|
@ -68,8 +73,11 @@ func (d *DevWebServer) Run(ctx context.Context) error {
|
|||
return c.String(http.StatusOK, assetdir)
|
||||
})
|
||||
|
||||
if _logger := ctx.Value("logger"); _logger != nil {
|
||||
myLogger = _logger.(*logger.Logger)
|
||||
}
|
||||
var err error
|
||||
assetHandler, err = assetserver.NewAssetHandler(ctx, assetServerConfig)
|
||||
assetHandler, err = assetserver.NewAssetHandler(assetServerConfig, myLogger)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
@ -101,7 +109,7 @@ func (d *DevWebServer) Run(ctx context.Context) error {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
assetServer, err := assetserver.NewDevAssetServer(ctx, assetHandler, wsHandler, bindingsJSON)
|
||||
assetServer, err := assetserver.NewDevAssetServer(assetHandler, wsHandler, bindingsJSON, ctx.Value("assetdir") != nil, myLogger, runtime.RuntimeAssetsBundle)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
26
v2/internal/frontend/runtime/assets.go
Normal file
26
v2/internal/frontend/runtime/assets.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//go:build !dev
|
||||
|
||||
package runtime
|
||||
|
||||
var RuntimeAssetsBundle = &RuntimeAssets{
|
||||
desktopIPC: DesktopIPC,
|
||||
runtimeDesktopJS: RuntimeDesktopJS,
|
||||
}
|
||||
|
||||
type RuntimeAssets struct {
|
||||
desktopIPC []byte
|
||||
websocketIPC []byte
|
||||
runtimeDesktopJS []byte
|
||||
}
|
||||
|
||||
func (r *RuntimeAssets) DesktopIPC() []byte {
|
||||
return r.desktopIPC
|
||||
}
|
||||
|
||||
func (r *RuntimeAssets) WebsocketIPC() []byte {
|
||||
return r.websocketIPC
|
||||
}
|
||||
|
||||
func (r *RuntimeAssets) RuntimeDesktopJS() []byte {
|
||||
return r.runtimeDesktopJS
|
||||
}
|
||||
27
v2/internal/frontend/runtime/assets_dev.go
Normal file
27
v2/internal/frontend/runtime/assets_dev.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//go:build dev
|
||||
|
||||
package runtime
|
||||
|
||||
var RuntimeAssetsBundle = &RuntimeAssets{
|
||||
desktopIPC: DesktopIPC,
|
||||
websocketIPC: WebsocketIPC,
|
||||
runtimeDesktopJS: RuntimeDesktopJS,
|
||||
}
|
||||
|
||||
type RuntimeAssets struct {
|
||||
desktopIPC []byte
|
||||
websocketIPC []byte
|
||||
runtimeDesktopJS []byte
|
||||
}
|
||||
|
||||
func (r *RuntimeAssets) DesktopIPC() []byte {
|
||||
return r.desktopIPC
|
||||
}
|
||||
|
||||
func (r *RuntimeAssets) WebsocketIPC() []byte {
|
||||
return r.websocketIPC
|
||||
}
|
||||
|
||||
func (r *RuntimeAssets) RuntimeDesktopJS() []byte {
|
||||
return r.runtimeDesktopJS
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package fs
|
|||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
|
|
@ -403,32 +402,3 @@ func FindFileInParents(path string, filename string) string {
|
|||
}
|
||||
return pathToFile
|
||||
}
|
||||
|
||||
// FindEmbedRootPath finds the root path in the embed FS. It's the directory which contains all the files.
|
||||
func FindEmbedRootPath(fsys embed.FS) (string, error) {
|
||||
stopErr := fmt.Errorf("files or multiple dirs found")
|
||||
|
||||
fPath := ""
|
||||
err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
fPath = path
|
||||
if entries, dErr := fs.ReadDir(fsys, path); dErr != nil {
|
||||
return dErr
|
||||
} else if len(entries) <= 1 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return stopErr
|
||||
})
|
||||
|
||||
if err != nil && err != stopErr {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fPath, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package gomod
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
|
|
|||
135
v2/internal/gomod/gomod_data_unix.go
Normal file
135
v2/internal/gomod/gomod_data_unix.go
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
//go:build darwin || linux
|
||||
|
||||
package gomod
|
||||
|
||||
const basic string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => /home/lea/wails/v2
|
||||
`
|
||||
const basicUpdated string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => /home/lea/wails/v2
|
||||
`
|
||||
|
||||
const multilineRequire = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => /home/lea/wails/v2
|
||||
`
|
||||
const multilineReplace = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => /home/lea/wails/v2
|
||||
`
|
||||
|
||||
const multilineReplaceNoVersion = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 => /home/lea/wails/v2
|
||||
`
|
||||
|
||||
const multilineReplaceNoVersionBlock = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 => /home/lea/wails/v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineReplaceBlock = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7 => /home/lea/wails/v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineRequireUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => /home/lea/wails/v2
|
||||
`
|
||||
|
||||
const multilineReplaceUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.20 => /home/lea/wails/v2
|
||||
`
|
||||
const multilineReplaceNoVersionUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 => /home/lea/wails/v2
|
||||
`
|
||||
const multilineReplaceNoVersionBlockUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 => /home/lea/wails/v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineReplaceBlockUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20 => /home/lea/wails/v2
|
||||
)
|
||||
`
|
||||
135
v2/internal/gomod/gomod_data_windows.go
Normal file
135
v2/internal/gomod/gomod_data_windows.go
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
//go:build windows
|
||||
|
||||
package gomod
|
||||
|
||||
const basic string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const basicUpdated string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
const multilineRequire = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplace = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
const multilineReplaceNoVersion = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
const multilineReplaceNoVersionBlock = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineReplaceBlock = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineRequireUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
const multilineReplaceUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.20 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplaceNoVersionUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplaceNoVersionBlockUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineReplaceBlockUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
//go:build windows
|
||||
|
||||
package gomod
|
||||
|
||||
import (
|
||||
|
|
@ -10,15 +8,6 @@ import (
|
|||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
const basic string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
func TestGetWailsVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -42,84 +31,6 @@ func TestGetWailsVersion(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
const basicUpdated string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
const multilineRequire = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplace = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
const multilineReplaceNoVersion = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
const multilineReplaceNoVersionBlock = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineReplaceBlock = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineRequireUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
|
||||
func TestUpdateGoModVersion(t *testing.T) {
|
||||
is2 := is.New(t)
|
||||
|
||||
|
|
@ -180,52 +91,6 @@ func TestGoModOutOfSync(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
const multilineReplaceUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.20 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplaceNoVersionUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplaceNoVersionBlockUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
const multilineReplaceBlockUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.20 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
const basicGo118 string = `module changeme
|
||||
|
||||
go 1.18
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
//go:build windows
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
|
|
|
|||
83
v2/internal/platform/systray/systray_linux.go
Normal file
83
v2/internal/platform/systray/systray_linux.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
//go:build linux
|
||||
|
||||
/*
|
||||
* Based on code originally from https://github.com/tadvi/systray. Copyright (C) 2019 The Systray Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package systray
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
type Systray struct {
|
||||
}
|
||||
|
||||
func (p *Systray) Close() {
|
||||
err := p.Stop()
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Systray) Update() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTitle is unused on Windows
|
||||
func (p *Systray) SetTitle(_ string) {}
|
||||
|
||||
func New() (*Systray, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *Systray) SetMenu(popupMenu *menu.Menu) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Systray) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Systray) OnLeftClick(fn func()) {
|
||||
}
|
||||
|
||||
func (p *Systray) OnRightClick(fn func()) {
|
||||
}
|
||||
|
||||
func (p *Systray) OnLeftDoubleClick(fn func()) {
|
||||
}
|
||||
|
||||
func (p *Systray) OnRightDoubleClick(fn func()) {
|
||||
}
|
||||
|
||||
func (p *Systray) OnMenuClose(fn func()) {
|
||||
}
|
||||
|
||||
func (p *Systray) OnMenuOpen(fn func()) {
|
||||
}
|
||||
|
||||
func (p *Systray) SetTooltip(tooltip string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Systray) Show() error {
|
||||
return p.setVisible(true)
|
||||
}
|
||||
|
||||
func (p *Systray) Hide() error {
|
||||
return p.setVisible(false)
|
||||
}
|
||||
|
||||
func (p *Systray) setVisible(visible bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Systray) SetIcons(lightModeIcon, darkModeIcon *options.SystemTrayIcon) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Systray) Run() error {
|
||||
return nil
|
||||
}
|
||||
91
v2/internal/platform/systray/systray_mac.go
Normal file
91
v2/internal/platform/systray/systray_mac.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//go:build darwin
|
||||
|
||||
package systray
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
var NotImplementedSysTray = errors.New("not implemented")
|
||||
|
||||
type Systray struct {
|
||||
}
|
||||
|
||||
func (p *Systray) Close() {
|
||||
err := p.Stop()
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Systray) Update() error {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) SetTitle(_ string) {}
|
||||
|
||||
func New() (*Systray, error) {
|
||||
return nil, NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) SetMenu(popupMenu *menu.Menu) (err error) {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) Stop() error {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) OnLeftClick(fn func()) {
|
||||
|
||||
}
|
||||
|
||||
func (p *Systray) OnRightClick(fn func()) {
|
||||
|
||||
}
|
||||
|
||||
func (p *Systray) OnLeftDoubleClick(fn func()) {
|
||||
|
||||
}
|
||||
|
||||
func (p *Systray) OnRightDoubleClick(fn func()) {
|
||||
|
||||
}
|
||||
|
||||
func (p *Systray) OnMenuClose(fn func()) {
|
||||
|
||||
}
|
||||
|
||||
func (p *Systray) OnMenuOpen(fn func()) {
|
||||
|
||||
}
|
||||
|
||||
func (p *Systray) SetTooltip(tooltip string) error {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) ShowMessage(title, msg string, bigIcon bool) error {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) Show() error {
|
||||
return p.setVisible(true)
|
||||
}
|
||||
|
||||
func (p *Systray) Hide() error {
|
||||
return p.setVisible(false)
|
||||
}
|
||||
|
||||
func (p *Systray) setVisible(visible bool) error {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) SetIcons(lightModeIcon, darkModeIcon *options.SystemTrayIcon) error {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
||||
func (p *Systray) Run() error {
|
||||
return NotImplementedSysTray
|
||||
}
|
||||
|
|
@ -4,10 +4,11 @@ package win32
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
||||
"golang.org/x/sys/windows"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -330,7 +331,7 @@ const (
|
|||
WS_EX_APPWINDOW = 0x00040000
|
||||
WS_OVERLAPPEDWINDOW = 0x00000000 | 0x00C00000 | 0x00080000 | 0x00040000 | 0x00020000 | 0x00010000
|
||||
WS_EX_NOREDIRECTIONBITMAP = 0x00200000
|
||||
CW_USEDEFAULT = 0x80000000
|
||||
CW_USEDEFAULT = ^0x7fffffff
|
||||
|
||||
NIM_ADD = 0x00000000
|
||||
NIM_MODIFY = 0x00000001
|
||||
|
|
|
|||
40
v2/internal/shell/env.go
Normal file
40
v2/internal/shell/env.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package shell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func UpsertEnv(env []string, key string, update func(v string) string) []string {
|
||||
newEnv := make([]string, len(env), len(env)+1)
|
||||
found := false
|
||||
for i := range env {
|
||||
if strings.HasPrefix(env[i], key+"=") {
|
||||
eqIndex := strings.Index(env[i], "=")
|
||||
val := env[i][eqIndex+1:]
|
||||
newEnv[i] = fmt.Sprintf("%s=%v", key, update(val))
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
newEnv[i] = env[i]
|
||||
}
|
||||
if !found {
|
||||
newEnv = append(newEnv, fmt.Sprintf("%s=%v", key, update("")))
|
||||
}
|
||||
return newEnv
|
||||
}
|
||||
|
||||
func RemoveEnv(env []string, key string) []string {
|
||||
newEnv := make([]string, 0, len(env))
|
||||
for _, e := range env {
|
||||
if strings.HasPrefix(e, key+"=") {
|
||||
continue
|
||||
}
|
||||
newEnv = append(newEnv, e)
|
||||
}
|
||||
return newEnv
|
||||
}
|
||||
|
||||
func SetEnv(env []string, key string, value string) []string {
|
||||
return UpsertEnv(env, key, func(_ string) string { return value })
|
||||
}
|
||||
67
v2/internal/shell/env_test.go
Normal file
67
v2/internal/shell/env_test.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package shell
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUpdateEnv(t *testing.T) {
|
||||
|
||||
env := []string{"one=1", "two=a=b", "three="}
|
||||
newEnv := UpsertEnv(env, "two", func(v string) string {
|
||||
return v + "+added"
|
||||
})
|
||||
newEnv = UpsertEnv(newEnv, "newVar", func(v string) string {
|
||||
return "added"
|
||||
})
|
||||
newEnv = UpsertEnv(newEnv, "three", func(v string) string {
|
||||
return "3"
|
||||
})
|
||||
newEnv = UpsertEnv(newEnv, "GOARCH", func(v string) string {
|
||||
return "amd64"
|
||||
})
|
||||
|
||||
if len(newEnv) != 5 {
|
||||
t.Errorf("expected: 5, got: %d", len(newEnv))
|
||||
}
|
||||
if newEnv[1] != "two=a=b+added" {
|
||||
t.Errorf("expected: \"two=a=b+added\", got: %q", newEnv[1])
|
||||
}
|
||||
if newEnv[2] != "three=3" {
|
||||
t.Errorf("expected: \"three=3\", got: %q", newEnv[2])
|
||||
}
|
||||
if newEnv[3] != "newVar=added" {
|
||||
t.Errorf("expected: \"newVar=added\", got: %q", newEnv[3])
|
||||
}
|
||||
if newEnv[4] != "GOARCH=amd64" {
|
||||
t.Errorf("expected: \"newVar=added\", got: %q", newEnv[4])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEnv(t *testing.T) {
|
||||
env := []string{"one=1", "two=a=b", "three="}
|
||||
newEnv := SetEnv(env, "two", "set")
|
||||
newEnv = SetEnv(newEnv, "newVar", "added")
|
||||
|
||||
if len(newEnv) != 4 {
|
||||
t.Errorf("expected: 4, got: %d", len(newEnv))
|
||||
}
|
||||
if newEnv[1] != "two=set" {
|
||||
t.Errorf("expected: \"two=set\", got: %q", newEnv[1])
|
||||
}
|
||||
if newEnv[3] != "newVar=added" {
|
||||
t.Errorf("expected: \"newVar=added\", got: %q", newEnv[3])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveEnv(t *testing.T) {
|
||||
env := []string{"one=1", "two=a=b", "three=3"}
|
||||
newEnv := RemoveEnv(env, "two")
|
||||
|
||||
if len(newEnv) != 2 {
|
||||
t.Errorf("expected: 2, got: %d", len(newEnv))
|
||||
}
|
||||
if newEnv[0] != "one=1" {
|
||||
t.Errorf("expected: \"one=1\", got: %q", newEnv[1])
|
||||
}
|
||||
if newEnv[1] != "three=3" {
|
||||
t.Errorf("expected: \"three=3\", got: %q", newEnv[3])
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +62,19 @@ func CreateCommand(directory string, command string, args ...string) *exec.Cmd {
|
|||
// RunCommand will run the given command + args in the given directory
|
||||
// Will return stdout, stderr and error
|
||||
func RunCommand(directory string, command string, args ...string) (string, string, error) {
|
||||
return RunCommandWithEnv(nil, directory, command, args...)
|
||||
}
|
||||
|
||||
// RunCommandWithEnv will run the given command + args in the given directory and using the specified env.
|
||||
//
|
||||
// Env specifies the environment of the process. Each entry is of the form "key=value".
|
||||
// If Env is nil, the new process uses the current process's environment.
|
||||
//
|
||||
// Will return stdout, stderr and error
|
||||
func RunCommandWithEnv(env []string, directory string, command string, args ...string) (string, string, error) {
|
||||
cmd := CreateCommand(directory, command, args...)
|
||||
cmd.Env = env
|
||||
|
||||
var stdo, stde bytes.Buffer
|
||||
cmd.Stdout = &stdo
|
||||
cmd.Stderr = &stde
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ func (y *Dnf) Packages() packagemap {
|
|||
{Name: "gtk3-devel", SystemPackage: true, Library: true},
|
||||
},
|
||||
"libwebkit": []*Package{
|
||||
{Name: "webkit2gtk4.0-devel", SystemPackage: true, Library: true},
|
||||
{Name: "webkit2gtk3-devel", SystemPackage: true, Library: true},
|
||||
// {Name: "webkitgtk3-devel", SystemPackage: true, Library: true},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -641,13 +641,19 @@ func (t *TypeScriptify) convertType(depth int, typeOf reflect.Type, customCode m
|
|||
err = builder.AddSimpleField(jsonFieldName, field, fldOpts)
|
||||
} else if field.Type.Kind() == reflect.Struct { // Struct:
|
||||
t.logf(depth, "- struct %s.%s (%s)", typeOf.Name(), field.Name, field.Type.String())
|
||||
typeScriptChunk, err := t.convertType(depth+1, field.Type, customCode)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if typeScriptChunk != "" {
|
||||
result = typeScriptChunk + "\n" + result
|
||||
|
||||
// Anonymous structures is ignored
|
||||
// It is possible to generate them but hard to generate correct name
|
||||
if field.Type.Name() != "" {
|
||||
typeScriptChunk, err := t.convertType(depth+1, field.Type, customCode)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if typeScriptChunk != "" {
|
||||
result = typeScriptChunk + "\n" + result
|
||||
}
|
||||
}
|
||||
|
||||
isKnownType := t.KnownStructs.Contains(getStructFQN(field.Type.String()))
|
||||
println("KnownStructs:", t.KnownStructs.Join("\t"))
|
||||
println(getStructFQN(field.Type.String()))
|
||||
|
|
@ -833,16 +839,24 @@ func (t *typeScriptClassBuilder) AddEnumField(fieldName string, field reflect.St
|
|||
|
||||
func (t *typeScriptClassBuilder) AddStructField(fieldName string, field reflect.StructField, isAnyType bool) {
|
||||
strippedFieldName := strings.ReplaceAll(fieldName, "?", "")
|
||||
namespace := strings.Split(field.Type.String(), ".")[0]
|
||||
fqname := "any"
|
||||
fqname := field.Type.Name()
|
||||
classname := "null"
|
||||
fqname = field.Type.Name()
|
||||
|
||||
namespace := strings.Split(field.Type.String(), ".")[0]
|
||||
|
||||
if namespace != t.namespace {
|
||||
fqname = field.Type.String()
|
||||
}
|
||||
|
||||
if !isAnyType {
|
||||
classname = fqname
|
||||
}
|
||||
|
||||
// Anonymous struct
|
||||
if field.Type.Name() == "" {
|
||||
classname = "Object"
|
||||
}
|
||||
|
||||
t.addField(fieldName, fqname, isAnyType)
|
||||
t.addInitializerFieldLine(strippedFieldName, fmt.Sprintf("this.convertValues(source[\"%s\"], %s)", strippedFieldName, classname))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package assetserver
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -13,11 +12,14 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
)
|
||||
|
||||
type Logger interface {
|
||||
Debug(message string, args ...interface{})
|
||||
Error(message string, args ...interface{})
|
||||
}
|
||||
|
||||
//go:embed defaultindex.html
|
||||
var defaultHTML []byte
|
||||
|
||||
|
|
@ -29,16 +31,12 @@ type assetHandler struct {
|
|||
fs iofs.FS
|
||||
handler http.Handler
|
||||
|
||||
logger *logger.Logger
|
||||
logger Logger
|
||||
|
||||
retryMissingFiles bool
|
||||
}
|
||||
|
||||
func NewAssetHandler(ctx context.Context, options assetserver.Options) (http.Handler, error) {
|
||||
var log *logger.Logger
|
||||
if _logger := ctx.Value("logger"); _logger != nil {
|
||||
log = _logger.(*logger.Logger)
|
||||
}
|
||||
func NewAssetHandler(options assetserver.Options, log Logger) (http.Handler, error) {
|
||||
|
||||
vfs := options.Assets
|
||||
if vfs != nil {
|
||||
|
|
@ -46,12 +44,12 @@ func NewAssetHandler(ctx context.Context, options assetserver.Options) (http.Han
|
|||
return nil, err
|
||||
}
|
||||
|
||||
subDir, err := fs.FindPathToFile(vfs, indexHTML)
|
||||
subDir, err := FindPathToFile(vfs, indexHTML)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
msg := "no `index.html` could be found in your Assets fs.FS"
|
||||
if embedFs, isEmbedFs := vfs.(embed.FS); isEmbedFs {
|
||||
rootFolder, _ := fs.FindEmbedRootPath(embedFs)
|
||||
rootFolder, _ := FindEmbedRootPath(embedFs)
|
||||
msg += fmt.Sprintf(", please make sure the embedded directory '%s' is correct and contains your assets", rootFolder)
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +98,7 @@ func (d *assetHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
d.logError("Unable to load file '%s': %s", filename, err)
|
||||
d.logError("Unable to handle request '%s': %s", url, err)
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
|
@ -129,13 +127,14 @@ func (d *assetHandler) serveFSFile(rw http.ResponseWriter, req *http.Request, fi
|
|||
return err
|
||||
}
|
||||
|
||||
url := req.URL.Path
|
||||
isDirectoryPath := url == "" || url[len(url)-1] == '/'
|
||||
if statInfo.IsDir() {
|
||||
url := req.URL.Path
|
||||
if url != "" && url[len(url)-1] != '/' {
|
||||
if !isDirectoryPath {
|
||||
// If the URL doesn't end in a slash normally a http.redirect should be done, but that currently doesn't work on
|
||||
// WebKit WebVies (macOS/Linux).
|
||||
// So we handle this as a file that could not be found.
|
||||
return os.ErrNotExist
|
||||
// WebKit WebViews (macOS/Linux).
|
||||
// So we handle this as a specific error
|
||||
return fmt.Errorf("a directory has been requested without a trailing slash, please add a trailing slash to your request")
|
||||
}
|
||||
|
||||
filename = path.Join(filename, indexHTML)
|
||||
|
|
@ -150,6 +149,8 @@ func (d *assetHandler) serveFSFile(rw http.ResponseWriter, req *http.Request, fi
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if isDirectoryPath {
|
||||
return fmt.Errorf("a file has been requested with a trailing slash, please remove the trailing slash from your request")
|
||||
}
|
||||
|
||||
var buf [512]byte
|
||||
|
|
@ -2,7 +2,6 @@ package assetserver
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
|
@ -10,8 +9,6 @@ import (
|
|||
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
)
|
||||
|
|
@ -21,41 +18,48 @@ const (
|
|||
ipcJSPath = "/wails/ipc.js"
|
||||
)
|
||||
|
||||
type RuntimeAssets interface {
|
||||
DesktopIPC() []byte
|
||||
WebsocketIPC() []byte
|
||||
RuntimeDesktopJS() []byte
|
||||
}
|
||||
|
||||
type AssetServer struct {
|
||||
handler http.Handler
|
||||
wsHandler http.Handler
|
||||
runtimeJS []byte
|
||||
ipcJS func(*http.Request) []byte
|
||||
|
||||
logger *logger.Logger
|
||||
logger Logger
|
||||
runtime RuntimeAssets
|
||||
|
||||
servingFromDisk bool
|
||||
appendSpinnerToBody bool
|
||||
}
|
||||
|
||||
func NewAssetServerMainPage(ctx context.Context, bindingsJSON string, options *options.App) (*AssetServer, error) {
|
||||
func NewAssetServerMainPage(bindingsJSON string, options *options.App, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
assetOptions, err := BuildAssetServerConfig(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAssetServer(ctx, bindingsJSON, assetOptions)
|
||||
return NewAssetServer(bindingsJSON, assetOptions, servingFromDisk, logger, runtime)
|
||||
}
|
||||
|
||||
func NewAssetServer(ctx context.Context, bindingsJSON string, options assetserver.Options) (*AssetServer, error) {
|
||||
handler, err := NewAssetHandler(ctx, options)
|
||||
func NewAssetServer(bindingsJSON string, options assetserver.Options, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
handler, err := NewAssetHandler(options, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewAssetServerWithHandler(ctx, handler, bindingsJSON)
|
||||
return NewAssetServerWithHandler(handler, bindingsJSON, servingFromDisk, logger, runtime)
|
||||
}
|
||||
|
||||
func NewAssetServerWithHandler(ctx context.Context, handler http.Handler, bindingsJSON string) (*AssetServer, error) {
|
||||
func NewAssetServerWithHandler(handler http.Handler, bindingsJSON string, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
var buffer bytes.Buffer
|
||||
if bindingsJSON != "" {
|
||||
buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n")
|
||||
}
|
||||
buffer.Write(runtime.RuntimeDesktopJS)
|
||||
buffer.Write(runtime.RuntimeDesktopJS())
|
||||
|
||||
result := &AssetServer{
|
||||
handler: handler,
|
||||
|
|
@ -65,11 +69,9 @@ func NewAssetServerWithHandler(ctx context.Context, handler http.Handler, bindin
|
|||
// If so, this means we are in dev mode and are serving assets off disk.
|
||||
// We indicate this through the `servingFromDisk` flag to ensure requests
|
||||
// aren't cached in dev mode.
|
||||
servingFromDisk: ctx.Value("assetdir") != nil,
|
||||
}
|
||||
|
||||
if _logger := ctx.Value("logger"); _logger != nil {
|
||||
result.logger = _logger.(*logger.Logger)
|
||||
servingFromDisk: servingFromDisk,
|
||||
logger: logger,
|
||||
runtime: runtime,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
@ -121,7 +123,7 @@ func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
d.writeBlob(rw, path, d.runtimeJS)
|
||||
|
||||
case ipcJSPath:
|
||||
content := runtime.DesktopIPC
|
||||
content := d.runtime.DesktopIPC()
|
||||
if d.ipcJS != nil {
|
||||
content = d.ipcJS(req)
|
||||
}
|
||||
|
|
@ -4,11 +4,8 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
@ -16,8 +13,8 @@ The assetserver for the dev mode.
|
|||
Depending on the UserAgent it injects a websocket based IPC script into `index.html` or the default desktop IPC. The
|
||||
default desktop IPC is injected when the webview accesses the devserver.
|
||||
*/
|
||||
func NewDevAssetServer(ctx context.Context, handler http.Handler, wsHandler http.Handler, bindingsJSON string) (*AssetServer, error) {
|
||||
result, err := NewAssetServerWithHandler(ctx, handler, bindingsJSON)
|
||||
func NewDevAssetServer(handler http.Handler, wsHandler http.Handler, bindingsJSON string, servingFromDisk bool, logger Logger, runtime RuntimeAssets) (*AssetServer, error) {
|
||||
result, err := NewAssetServerWithHandler(handler, bindingsJSON, servingFromDisk, logger, runtime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -26,9 +23,9 @@ func NewDevAssetServer(ctx context.Context, handler http.Handler, wsHandler http
|
|||
result.appendSpinnerToBody = true
|
||||
result.ipcJS = func(req *http.Request) []byte {
|
||||
if strings.Contains(req.UserAgent(), WailsUserAgentValue) {
|
||||
return runtime.DesktopIPC
|
||||
return runtime.DesktopIPC()
|
||||
}
|
||||
return runtime.WebsocketIPC
|
||||
return runtime.WebsocketIPC()
|
||||
}
|
||||
|
||||
return result, nil
|
||||
75
v2/pkg/assetserver/fs.go
Normal file
75
v2/pkg/assetserver/fs.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package assetserver
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FindEmbedRootPath finds the root path in the embed FS. It's the directory which contains all the files.
|
||||
func FindEmbedRootPath(fsys embed.FS) (string, error) {
|
||||
stopErr := fmt.Errorf("files or multiple dirs found")
|
||||
|
||||
fPath := ""
|
||||
err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
fPath = path
|
||||
if entries, dErr := fs.ReadDir(fsys, path); dErr != nil {
|
||||
return dErr
|
||||
} else if len(entries) <= 1 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return stopErr
|
||||
})
|
||||
|
||||
if err != nil && err != stopErr {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fPath, nil
|
||||
}
|
||||
|
||||
func FindPathToFile(fsys fs.FS, file string) (string, error) {
|
||||
stat, _ := fs.Stat(fsys, file)
|
||||
if stat != nil {
|
||||
return ".", nil
|
||||
}
|
||||
var indexFiles []string
|
||||
err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.HasSuffix(path, file) {
|
||||
indexFiles = append(indexFiles, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(indexFiles) > 1 {
|
||||
selected := indexFiles[0]
|
||||
for _, f := range indexFiles {
|
||||
if len(f) < len(selected) {
|
||||
selected = f
|
||||
}
|
||||
}
|
||||
path, _ := filepath.Split(selected)
|
||||
return path, nil
|
||||
}
|
||||
if len(indexFiles) > 0 {
|
||||
path, _ := filepath.Split(indexFiles[0])
|
||||
return path, nil
|
||||
}
|
||||
return "", fmt.Errorf("%s: %w", file, os.ErrNotExist)
|
||||
}
|
||||
|
|
@ -62,7 +62,12 @@ func GenerateBindings(options Options) (string, error) {
|
|||
_ = os.Remove(filename)
|
||||
}()
|
||||
|
||||
stdout, stderr, err = shell.RunCommand(workingDirectory, filename, "-tsprefix", options.TsPrefix, "-tssuffix", options.TsSuffix)
|
||||
// Set environment variables accordingly
|
||||
env := os.Environ()
|
||||
env = shell.SetEnv(env, "tsprefix", options.TsPrefix)
|
||||
env = shell.SetEnv(env, "tssuffix", options.TsSuffix)
|
||||
|
||||
stdout, stderr, err = shell.RunCommandWithEnv(env, workingDirectory, filename)
|
||||
if err != nil {
|
||||
return stdout, fmt.Errorf("%s\n%s\n%s", stdout, stderr, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,8 +302,8 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||
cmd.Env = os.Environ() // inherit env
|
||||
|
||||
if options.Platform != "windows" {
|
||||
// Use upsertEnv so we don't overwrite user's CGO_CFLAGS
|
||||
cmd.Env = upsertEnv(cmd.Env, "CGO_CFLAGS", func(v string) string {
|
||||
// Use shell.UpsertEnv so we don't overwrite user's CGO_CFLAGS
|
||||
cmd.Env = shell.UpsertEnv(cmd.Env, "CGO_CFLAGS", func(v string) string {
|
||||
if options.Platform == "darwin" {
|
||||
if v != "" {
|
||||
v += " "
|
||||
|
|
@ -312,8 +312,8 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||
}
|
||||
return v
|
||||
})
|
||||
// Use upsertEnv so we don't overwrite user's CGO_CXXFLAGS
|
||||
cmd.Env = upsertEnv(cmd.Env, "CGO_CXXFLAGS", func(v string) string {
|
||||
// Use shell.UpsertEnv so we don't overwrite user's CGO_CXXFLAGS
|
||||
cmd.Env = shell.UpsertEnv(cmd.Env, "CGO_CXXFLAGS", func(v string) string {
|
||||
if v != "" {
|
||||
v += " "
|
||||
}
|
||||
|
|
@ -321,7 +321,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||
return v
|
||||
})
|
||||
|
||||
cmd.Env = upsertEnv(cmd.Env, "CGO_ENABLED", func(v string) string {
|
||||
cmd.Env = shell.UpsertEnv(cmd.Env, "CGO_ENABLED", func(v string) string {
|
||||
return "1"
|
||||
})
|
||||
if options.Platform == "darwin" {
|
||||
|
|
@ -338,7 +338,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||
}
|
||||
addUTIFramework := majorVersion >= 11
|
||||
// Set the minimum Mac SDK to 10.13
|
||||
cmd.Env = upsertEnv(cmd.Env, "CGO_LDFLAGS", func(v string) string {
|
||||
cmd.Env = shell.UpsertEnv(cmd.Env, "CGO_LDFLAGS", func(v string) string {
|
||||
if v != "" {
|
||||
v += " "
|
||||
}
|
||||
|
|
@ -352,11 +352,11 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||
}
|
||||
}
|
||||
|
||||
cmd.Env = upsertEnv(cmd.Env, "GOOS", func(v string) string {
|
||||
cmd.Env = shell.UpsertEnv(cmd.Env, "GOOS", func(v string) string {
|
||||
return options.Platform
|
||||
})
|
||||
|
||||
cmd.Env = upsertEnv(cmd.Env, "GOARCH", func(v string) string {
|
||||
cmd.Env = shell.UpsertEnv(cmd.Env, "GOARCH", func(v string) string {
|
||||
return options.Arch
|
||||
})
|
||||
|
||||
|
|
@ -608,22 +608,3 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
|||
pterm.Println("Done.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func upsertEnv(env []string, key string, update func(v string) string) []string {
|
||||
newEnv := make([]string, len(env), len(env)+1)
|
||||
found := false
|
||||
for i := range env {
|
||||
if strings.HasPrefix(env[i], key+"=") {
|
||||
eqIndex := strings.Index(env[i], "=")
|
||||
val := env[i][eqIndex+1:]
|
||||
newEnv[i] = fmt.Sprintf("%s=%v", key, update(val))
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
newEnv[i] = env[i]
|
||||
}
|
||||
if !found {
|
||||
newEnv = append(newEnv, fmt.Sprintf("%s=%v", key, update("")))
|
||||
}
|
||||
return newEnv
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,40 +2,6 @@ package build
|
|||
|
||||
import "testing"
|
||||
|
||||
func TestUpdateEnv(t *testing.T) {
|
||||
|
||||
env := []string{"one=1", "two=a=b", "three="}
|
||||
newEnv := upsertEnv(env, "two", func(v string) string {
|
||||
return v + "+added"
|
||||
})
|
||||
newEnv = upsertEnv(newEnv, "newVar", func(v string) string {
|
||||
return "added"
|
||||
})
|
||||
newEnv = upsertEnv(newEnv, "three", func(v string) string {
|
||||
return "3"
|
||||
})
|
||||
newEnv = upsertEnv(newEnv, "GOARCH", func(v string) string {
|
||||
return "amd64"
|
||||
})
|
||||
|
||||
if len(newEnv) != 5 {
|
||||
t.Errorf("expected: 5, got: %d", len(newEnv))
|
||||
}
|
||||
if newEnv[1] != "two=a=b+added" {
|
||||
t.Errorf("expected: \"two=a=b+added\", got: %q", newEnv[1])
|
||||
}
|
||||
if newEnv[2] != "three=3" {
|
||||
t.Errorf("expected: \"three=3\", got: %q", newEnv[2])
|
||||
}
|
||||
if newEnv[3] != "newVar=added" {
|
||||
t.Errorf("expected: \"newVar=added\", got: %q", newEnv[3])
|
||||
}
|
||||
if newEnv[4] != "GOARCH=amd64" {
|
||||
t.Errorf("expected: \"newVar=added\", got: %q", newEnv[4])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_commandPrettifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/pterm/pterm"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/staticanalysis"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/bindings"
|
||||
|
||||
|
|
@ -333,17 +333,17 @@ func execBuildApplication(builder Builder, options *Options) (string, error) {
|
|||
}
|
||||
|
||||
if options.Platform == "windows" {
|
||||
const expWebView2Loader = "exp_gowebview2loader"
|
||||
const nativeWebView2Loader = "native_webview2loader"
|
||||
|
||||
message := ""
|
||||
tags := options.UserTags
|
||||
if lo.Contains(tags, expWebView2Loader) {
|
||||
message = "Thanks for testing the new experimental Go native WebView2Loader. Please report your feedback and any bugs you think might be related to using the new loader: https://github.com/wailsapp/wails/issues/2004"
|
||||
if lo.Contains(tags, nativeWebView2Loader) {
|
||||
message := "You are using the legacy native WebView2Loader. This loader will be deprecated in the near future. Please report any bugs related to the new loader: https://github.com/wailsapp/wails/issues/2004"
|
||||
pterm.Warning.Println(message)
|
||||
} else {
|
||||
tags = append(tags, expWebView2Loader)
|
||||
message = fmt.Sprintf("An experimental Go native WebView2Loader is available. We would love to hear your feedback about it and invite you to test it by building with `-tags %s`", strings.Join(tags, ","))
|
||||
tags = append(tags, nativeWebView2Loader)
|
||||
message := fmt.Sprintf("Wails is now using the new Go WebView2Loader. If you encounter any issues with it, please report them to https://github.com/wailsapp/wails/issues/2004. You could also use the old legacy loader with `-tags %s`, but keep in mind this will be deprecated in the near future.", strings.Join(tags, ","))
|
||||
pterm.Info.Println(message)
|
||||
}
|
||||
pterm.Info.Println(message)
|
||||
}
|
||||
|
||||
if options.Platform == "darwin" && options.Mode == Debug {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for
|
|||
- [wails-vite-vue-the-works](https://github.com/codydbentley/wails-vite-vue-the-works) - Vue 3 TypeScript with Vite, Vuex, Vue Router, Sass, and ESLint + Prettier
|
||||
- [wails-template-quasar-js](https://github.com/sgosiaco/wails-template-quasar-js) - A template using JavaScript + Quasar V2 (Vue 3, Vite, Sass, Pinia, ESLint, Prettier)
|
||||
- [wails-template-quasar-ts](https://github.com/sgosiaco/wails-template-quasar-ts) - A template using TypeScript + Quasar V2 (Vue 3, Vite, Sass, Pinia, ESLint, Prettier, Composition API with <script setup>)
|
||||
- [wails-template-naive](https://github.com/tk103331/wails-template-naive) - Wails template based on Naive UI (A Vue 3 Component Library)
|
||||
|
||||
## Angular
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for
|
|||
| -windowsconsole | Keep the console window for Windows builds | |
|
||||
| -obfuscate | Obfuscate the application using [garble](https://github.com/burrowers/garble) | false |
|
||||
| -garbleargs | Arguments to pass to garble | `-literals -tiny -seed=random` |
|
||||
| -nosyncgomod | Do not sync go.mod with the Wails version | false |
|
||||
|
||||
For a detailed description of the `webview2` flag, please refer to the [Windows](../guides/windows.mdx) Guide.
|
||||
|
||||
|
|
@ -188,6 +189,9 @@ Your system is ready for Wails development!
|
|||
| -save | Saves the given `assetdir`, `reloaddirs`, `wailsjsdir`, `debounce`, `devserver` and `frontenddevserverurl` flags in `wails.json` to become the defaults for subsequent invocations. | |
|
||||
| -race | Build with Go's race detector | false |
|
||||
| -s | Skip building the frontend | false |
|
||||
| -nosyncgomod | Do not sync go.mod with the Wails version | false |
|
||||
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
|||
|
|
@ -258,11 +258,11 @@ Not all features of an `http.Request` are currently supported, please see the fo
|
|||
| DELETE | ✅ | ✅ | ✅ [^1] |
|
||||
| Request Headers | ✅ | ✅ | ✅ [^1] |
|
||||
| Request Body | ✅ | ✅ | ❌ |
|
||||
| Request Body Streaming | ❌ | ❌ | ❌ |
|
||||
| Request Body Streaming | ✅ | ✅ | ❌ |
|
||||
| Response StatusCodes | ✅ | ✅ | ✅ [^1] |
|
||||
| Response Headers | ✅ | ✅ | ✅ [^1] |
|
||||
| Response Body | ✅ | ✅ | ✅ |
|
||||
| Response Body Streaming | ❌ | ❌ | ✅ |
|
||||
| Response Body Streaming | ❌ | ✅ | ✅ |
|
||||
| WebSockets | ❌ | ❌ | ❌ |
|
||||
| HTTP Redirects 30x | ✅ | ❌ | ❌ |
|
||||
|
||||
|
|
|
|||
|
|
@ -225,12 +225,8 @@ module.exports = async function configCreatorAsync() {
|
|||
href: "https://twitter.com/wailsapp",
|
||||
},
|
||||
{
|
||||
label: "Slack",
|
||||
href: "https://gophers.slack.com/messages/CJ4P9F7MZ/",
|
||||
},
|
||||
{
|
||||
label: "Slack invite",
|
||||
href: "https://invite.slack.golangbridge.org/",
|
||||
label: "Discord",
|
||||
href: "https://discord.gg/JDdSxwjhGf",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -411,5 +411,13 @@
|
|||
"theme.SearchModal.placeholder": {
|
||||
"message": "Search docs",
|
||||
"description": "The placeholder of the input of the DocSearch pop-up modal"
|
||||
},
|
||||
"theme.docs.sidebar.closeSidebarButtonAriaLabel": {
|
||||
"message": "Close navigation bar",
|
||||
"description": "The ARIA label for close button of mobile sidebar"
|
||||
},
|
||||
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": {
|
||||
"message": "Toggle navigation bar",
|
||||
"description": "The ARIA label for hamburger menu button of mobile navigation"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"version.label": {
|
||||
"message": "v2.3.0",
|
||||
"description": "The label for version v2.3.0"
|
||||
},
|
||||
"sidebar.docs.category.Getting Started": {
|
||||
"message": "Getting Started",
|
||||
"description": "The label for category Getting Started in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Reference": {
|
||||
"message": "Reference",
|
||||
"description": "The label for category Reference in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Runtime": {
|
||||
"message": "Runtime",
|
||||
"description": "The label for category Runtime in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Community": {
|
||||
"message": "Community",
|
||||
"description": "The label for category Community in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Showcase": {
|
||||
"message": "Showcase",
|
||||
"description": "The label for category Showcase in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Guides": {
|
||||
"message": "Guides",
|
||||
"description": "The label for category Guides in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Tutorials": {
|
||||
"message": "Tutorials",
|
||||
"description": "The label for category Tutorials in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.link.Contributing": {
|
||||
"message": "Contributing",
|
||||
"description": "The label for link Contributing in sidebar docs, linking to /community-guide#ways-of-contributing"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"version.label": {
|
||||
"message": "v2.3.1",
|
||||
"description": "The label for version v2.3.1"
|
||||
},
|
||||
"sidebar.docs.category.Getting Started": {
|
||||
"message": "Getting Started",
|
||||
"description": "The label for category Getting Started in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Reference": {
|
||||
"message": "Reference",
|
||||
"description": "The label for category Reference in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Runtime": {
|
||||
"message": "Runtime",
|
||||
"description": "The label for category Runtime in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Community": {
|
||||
"message": "Community",
|
||||
"description": "The label for category Community in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Showcase": {
|
||||
"message": "Showcase",
|
||||
"description": "The label for category Showcase in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Guides": {
|
||||
"message": "Guides",
|
||||
"description": "The label for category Guides in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.category.Tutorials": {
|
||||
"message": "Tutorials",
|
||||
"description": "The label for category Tutorials in sidebar docs"
|
||||
},
|
||||
"sidebar.docs.link.Contributing": {
|
||||
"message": "Contributing",
|
||||
"description": "The label for link Contributing in sidebar docs, linking to /community-guide#ways-of-contributing"
|
||||
}
|
||||
}
|
||||
|
|
@ -50,5 +50,9 @@
|
|||
"link.item.label.Awesome": {
|
||||
"message": "Awesome",
|
||||
"description": "The label of footer link with label=Awesome linking to https://github.com/wailsapp/awesome-wails"
|
||||
},
|
||||
"link.item.label.Discord": {
|
||||
"message": "Discord",
|
||||
"description": "The label of footer link with label=Discord linking to https://discord.gg/JDdSxwjhGf"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Liens
|
||||
|
||||
Cette page sert de liste pour les liens liés à la communauté. Veuillez soumettre une PR (cliquez sur `Modifier cette page` en bas) pour soumettre des liens.
|
||||
|
||||
## Awesome Wails
|
||||
|
||||
La [liste définitive](https://github.com/wailsapp/awesome-wails) de liens relatifs à Wails.
|
||||
|
||||
## Canaux de support
|
||||
|
||||
- [Gophers Slack Channel](https://gophers.slack.com/messages/CJ4P9F7MZ/)
|
||||
- [Gophers Slack Channel Invite](https://invite.slack.golangbridge.org/)
|
||||
- [Github Issues](https://github.com/wailsapp/wails/issues)
|
||||
- [canal de discussion sur la bêta v2](https://github.com/wailsapp/wails/discussions/828)
|
||||
|
||||
## Réseaux sociaux
|
||||
|
||||
- [Twitter](https://twitter.com/wailsapp)
|
||||
- [Groupe QQ pour la communauté chinoise de Wails](https://qm.qq.com/cgi-bin/qm/qr?k=PmIURne5hFGNd7QWzW5qd6FV-INEjNJv&jump_from=webapi) - Numéro de groupe : 1067173054
|
||||
|
||||
## Autres tutoriels et articles
|
||||
- [Construction d'un Panneau d'Affichage](https://blog.customct.com/building-bulletin-board)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# EncryptEasy
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/encrypteasy.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
**[EncryptEasy](https://www.encrypteasy.app) est un outil de chiffrement PGP simple et facile à utiliser, qui gère toutes vos clés et celles de vos contacts. Le chiffrement devrait être simple. Développé avec Wails.**
|
||||
|
||||
Chiffrer les messages à l'aide de PGP est la norme de l'industrie. Tout le monde a une clé privée et publique. Votre clé privée, eh bien, doit être privée afin que vous seul puissiez lire les messages. Votre clé publique est distribuée à toute personne qui veut vous envoyer des messages secrets, chiffrés. Gérer les clés, chiffrer les messages et déchiffrer les messages devrait être une expérience agréable. EncryptEasy a pour but de vous simplifier la tâche.
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Utilitaire d'exportation FileHound
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/filehound.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[L'utilitaire d'exportation FileHound](https://www.filehound.co.uk/) est une plate-forme de gestion de documents cloud conçue pour la conservation sécurisée de fichiers, l'automatisation des processus métier et les capacités de SmartCapture.
|
||||
|
||||
L'utilitaire d'exportation FileHound permet aux administrateurs FileHound d'exécuter des tâches sécurisées d'extraction de documents et de données à des fins alternatives de sauvegarde et de récupération. Cette application téléchargera tous les documents et/ou métadonnées enregistrés dans FileHound en fonction des filtres que vous avez choisis. Les métadonnées seront exportées dans les formats JSON et XML.
|
||||
|
||||
Backend construit avec: Go 1.15 Wails 1.11.0 go-sqlite3 1.14.6 go-linq 3.2
|
||||
|
||||
Frontend avec: Vue 2.6.11 Vuex 3.4.0 TypeScript Tailwind 1.9.6
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Minecraft Updater
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img
|
||||
src={
|
||||
require("@site/static/img/showcase/minecraft-mod-updater.webp").default
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Minecraft Updater](https://github.com/Gurkengewuerz/MinecraftModUpdater) est un outil utilitaire pour mettre à jour et synchroniser les mods Minecraft pour votre base d'utilisateurs. Il a été conçu en utilisant Wails2 et React avec [antd](https://ant.design/) comme framework frontend.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Modal File Manager
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img
|
||||
src={require("@site/static/img/showcase/modalfilemanager.webp").default}
|
||||
/>
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Modal File Manager](https://github.com/raguay/ModalFileManager) est un gestionnaire de fichiers à double volet utilisant des technologies web. Mon design original était basé sur NW.js et peut être trouvé [ici](https://github.com/raguay/ModalFileManager-NWjs). Cette version utilise le même code frontend basé sur Svelte (mais il a été grandement modifié depuis le départ de NW.js), mais le backend est une implémentation de [Wails 2](https://wails.io/). En utilisant cette implémentation, je n'utilise plus de commandes en ligne de commande `rm`, `cp`, etc. Il est entièrement codé en utilisant Go et fonctionne beaucoup plus rapidement que les versions précédentes.
|
||||
|
||||
Ce gestionnaire de fichiers est conçu autour du même principe que Vim: l'état est contrôlé par des actions via le clavier. Le nombre d'états n'est pas fixe, mais très programmable. Par conséquent, un nombre infini de configurations de clavier qui peuvent être créées et utilisées. C'est la principale différence par rapport aux autres gestionnaires de fichiers.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Molley Wallet
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/mollywallet.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Molly Wallet](https://github.com/grvlle/constellation_wallet/) le portefeuille officiel $DAG du Constellation Network. Cela permettra aux utilisateurs d'interagir avec le réseau Hypergraph de différentes manières, sans se limiter à la production de transactions en $DAG.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# October
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/october.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Octobre](https://october.utf9k.net) est une petite application Wails qui rend vraiment facile d'extraire les surlignements de [Kobo eReaders](https://en.wikipedia.org/wiki/Kobo_eReader) puis de les transférer vers [Readwise](https://readwise.io).
|
||||
|
||||
Il a une taille relativement petite avec toutes les versions de la plate-forme pesant en moins de 10 Mo, et c'est sans activer la [compression UPX](https://upx.github.io/)!
|
||||
|
||||
En revanche, les précédentes tentatives de l'auteur avec Electron ont rapidement gonflé à plusieurs centaines de mégaoctets.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Optimus
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/optimus.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Optimus](https://github.com/splode/optimus) est une application d'optimisation d'image de bureau. Il supporte la conversion et la compression entre les formats d’images WebP, JPEG et PNG.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Portfall
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/portfall.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Portfall](https://github.com/rekon-oss/portfall) - Un portail de redirection de port k8 pour un accès facile à toutes les interfaces de votre instance
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Restic Browser
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img
|
||||
src={require("@site/static/img/showcase/restic-browser-2.png").default}
|
||||
/>
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Restic-Browser](https://github.com/emuell/restic-browser) - Une interface de sauvegarde simple et multiplateforme [restic](https://github.com/restic/restic) pour la navigation et la restauration de dépôts restic.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# RiftShare
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/riftshare-main.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
Partage de fichiers facile, sécurisé et gratuit pour tout le monde. Apprenez-en plus sur [Riftshare.app](https://riftshare.app)
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
- Partage facile et sécurisé de fichiers entre ordinateurs à la fois sur le réseau local et via Internet
|
||||
- Supporte l'envoi de fichiers ou de répertoires de manière sécurisée par le protocole [magic wormhole](https://magic-wormhole.readthedocs.io/en/latest/)
|
||||
- Compatible avec toutes les autres applications utilisant magic wormhole (magic-wormhole or wormhole-william CLI, wormhole-gui, etc.)
|
||||
- Compression automatique de plusieurs fichiers sélectionnés à envoyer en même temps
|
||||
- Animations complètes, barre de progression et support d'annulation pour l'envoi et la réception
|
||||
- Sélection de fichier natif au système d'exploitation
|
||||
- Ouvrir les fichiers en un seul clic une fois reçus
|
||||
- Mise à jour automatique - ne vous inquiétez pas d'avoir la dernière version!
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# ScriptBar
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/scriptbar.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Barre de scripts](https://GitHub.com/raguay/ScriptBarApp) est un programme pour afficher la sortie du serveur [Node-Red](https://nodered.org) intégré dans l'application [EmailIt](https://GitHub.com/raguay/EmailIt). Il affiche également la sortie des scripts sur votre système. ScriptBar ne les met pas dans la barre de menus, mais les a tous dans une fenêtre convenable pour une visualisation facile. Vous pouvez avoir plusieurs onglets pour voir plusieurs choses différentes. Vous pouvez également conserver les liens vers vos sites Web les plus visités.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Surge
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/surge.png").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Surge](https://getsurge.io/) est une application de partage de fichiers p2p conçue pour utiliser les technologies blockchain afin d'activer les transferts de fichiers 100 % anonymes. Surge est chiffré de bout en bout, décentralisé et open source.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Wally
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/wally.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Wally](https://ergodox-ez.com/pages/wally) est le flasheur officiel du firmware pour les claviers [Ergodox](https://ergodox-ez.com/). C'est un excellent exemple de ce que vous pouvez réaliser avec Wails : la capacité de combiner la puissance de Go et les riches outils graphiques du monde du développement web.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Wombat
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/wombat.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Wombat](https://github.com/rogchap/wombat) est un client gRPC multi-plateforme.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Ytd
|
||||
|
||||
```mdx-code-block
|
||||
<p style={{ "text-align": "center" }}>
|
||||
<img src={require("@site/static/img/showcase/ytd.webp").default} />
|
||||
<br />
|
||||
</p>
|
||||
```
|
||||
|
||||
[Ytd](https://github.com/marcio199226/ytd/tree/v2-wails) est une application pour télécharger des pistes depuis youtube, créer des listes de lecture hors ligne et les partager avec vos amis, vos amis seront en mesure de lire vos playlists ou de les télécharger pour l'écoute hors ligne, a un lecteur intégré.
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Modèles
|
||||
|
||||
Cette page sert de liste pour les modèles supportés par la communauté. Veuillez soumettre une PR (cliquez sur `Modifier cette page` en bas) pour inclure vos modèles. Pour construire votre propre modèle, veuillez consulter le guide [Modèles](../guides/templates.mdx).
|
||||
|
||||
Pour utiliser ces modèles, exécutez `wails init -n "Votre nom de projet" -t [le lien ci-dessous[@version]]`
|
||||
|
||||
S'il n'y a pas de suffixe de version, la branche principale du modèle de code sera alors utilisé par défaut. S'il y a un suffixe de version, le modèle de code correspondant au tag de cette version sera utilisé.
|
||||
|
||||
Exemple : `wails init -n "Votre nom de projet" -t https://github.com/misitebao/wails-template-vue`
|
||||
|
||||
:::warning Attention
|
||||
|
||||
**Le projet Wails n'entretient pas, et n'est pas responsable des modèles de tierces parties!**
|
||||
|
||||
Si vous n'êtes pas sûr d'un modèle, inspectez `package.json` et `wails.json` pour savoir quels scripts sont exécutés et quels paquets sont installés.
|
||||
|
||||
:::
|
||||
|
||||
## Vue
|
||||
|
||||
- [wails-template-vue](https://github.com/misitebao/wails-template-vue) - Modèle de Wails basé sur Vue (TypeScript intégré, thème sombre, internationalisation, routage de page unique, TailwindCSS)
|
||||
- [wails-vite-vue-ts](https://github.com/codydbentley/wails-vite-vue-ts) - Vue 3 TypeScript avec Vite (et instructions pour ajouter des fonctionnalités)
|
||||
- [wails-vite-vue-the-works](https://github.com/codydbentley/wails-vite-vue-the-works) - Vue 3 TypeScript avec Vite, Vuex, Vue Router, Sass, et ESLint + Prettier
|
||||
- [wails-template-quasar-js](https://github.com/sgosiaco/wails-template-quasar-js) - Un modèle utilisant JavaScript + Quasar V2 (Vue 3, Vite, Sass, Pinia, ESLint, Prettier)
|
||||
- [wails-template-quasar-ts](https://github.com/sgosiaco/wails-template-quasar-ts) - Un modèle utilisant TypeScript + Quasar V2 (Vue 3, Vite, Sass, Pinia, ESLint, Prettier, Composition API avec <script setup>)
|
||||
|
||||
## Angular
|
||||
|
||||
- [wails-angular-template](https://github.com/TAINCER/wails-angular-template) - Angular avec TypeScript, Sass, rechargement à chaud, découpage dynamique de code et i18n
|
||||
|
||||
## React
|
||||
|
||||
- [wails-react-template](https://github.com/AlienRecall/wails-react-template) - Un modèle utilisant reactjs
|
||||
- [wails-react-template](https://github.com/flin7/wails-react-template) - Un modèle minimal pour React qui supporte le développement en direct
|
||||
- [wails-template-nextjs](https://github.com/LGiki/wails-template-nextjs) - Un modèle utilisant Next.js et TypeScript
|
||||
- [wails-vite-react-ts-tailwind-template](https://github.com/hotafrika/wails-vite-react-ts-tailwind-template) - Un modèle pour React + TypeScript + Vite + TailwindCSS
|
||||
|
||||
## Svelte
|
||||
|
||||
- [wails-svelte-template](https://github.com/raitonoberu/wails-svelte-template) - Un modèle utilisant Svelte
|
||||
- [wails-vite-svelte-template](https://github.com/BillBuilt/wails-vite-svelte-template) - Un modèle utilisant Svelte et Vite
|
||||
- [wails-vite-svelte-tailwind-template](https://github.com/BillBuilt/wails-vite-svelte-tailwind-template) - Un modèle utilisant Svelte et Vite avec TailwindCSS v3
|
||||
- [wails-sveltekit-template](https://github.com/h8gi/wails-sveltekit-template) - Un modèle utilisant SvelteKit
|
||||
|
||||
## Elm
|
||||
|
||||
- [wails-elm-template](https://github.com/benjamin-thomas/wails-elm-template) - Développez votre application GUI avec de la programmation fonctionnelle et une configuration de développement en direct :tada: :rocket:
|
||||
- [wails-template-elm-tailwind](https://github.com/rnice01/wails-template-elm-tailwind) - Combine les puissances :muscle: d'Elm + Tailwind CSS + Wails ! Rechargement automatique pris en charge.
|
||||
|
||||
## Pure JavaScript (Vanilla)
|
||||
|
||||
- [wails-pure-js-template](https://github.com/KiddoV/wails-pure-js-template) - Un modèle avec rien que du JavaScript, du HTML et du CSS de base
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue