From a0b2ab7c0a61d4d507bf8e2a662ee9066c1d3beb Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sat, 14 Dec 2024 07:50:55 +1100 Subject: [PATCH] Fix default index.html serving. Support multi-language default. Remove default page from production builds. Improve defaultindex.html. --- v2/pkg/assetserver/assethandler.go | 11 +- v3/examples/dialogs-basic/.hidden_file | 0 v3/examples/dialogs-basic/README.md | 36 ++ v3/examples/dialogs-basic/main.go | 260 +++++++++++++ v3/examples/dialogs-basic/test.txt | 1 + .../dialogs-basic/wails-logo-small.jpg | Bin 0 -> 2662 bytes .../dialogs-basic/wails-logo-small.png | Bin 0 -> 4864 bytes v3/internal/assetserver/assetserver.go | 10 +- v3/internal/assetserver/assetserver_dev.go | 37 ++ .../assetserver/assetserver_production.go | 4 + v3/internal/assetserver/build_dev.go | 7 - v3/internal/assetserver/defaultindex.html | 39 -- .../assetserver/defaults/index.en.html | 350 ++++++++++++++++++ .../assetserver/defaults/index.zh.html | 302 +++++++++++++++ v3/pkg/application/dialogs_darwin_delegate.h | 8 +- v3/pkg/application/dialogs_darwin_delegate.m | 26 +- 16 files changed, 1025 insertions(+), 66 deletions(-) create mode 100644 v3/examples/dialogs-basic/.hidden_file create mode 100644 v3/examples/dialogs-basic/README.md create mode 100644 v3/examples/dialogs-basic/main.go create mode 100644 v3/examples/dialogs-basic/test.txt create mode 100644 v3/examples/dialogs-basic/wails-logo-small.jpg create mode 100644 v3/examples/dialogs-basic/wails-logo-small.png delete mode 100644 v3/internal/assetserver/defaultindex.html create mode 100644 v3/internal/assetserver/defaults/index.en.html create mode 100644 v3/internal/assetserver/defaults/index.zh.html diff --git a/v2/pkg/assetserver/assethandler.go b/v2/pkg/assetserver/assethandler.go index b8e2df076..b56a5d033 100644 --- a/v2/pkg/assetserver/assethandler.go +++ b/v2/pkg/assetserver/assethandler.go @@ -21,9 +21,6 @@ type Logger interface { Error(message string, args ...interface{}) } -//go:embed defaultindex.html -var defaultHTML []byte - const ( indexHTML = "index.html" ) @@ -120,7 +117,9 @@ func (d *assetHandler) serveFSFile(rw http.ResponseWriter, req *http.Request, fi if err != nil { return err } - defer file.Close() + defer func() { + _ = file.Close() + }() statInfo, err := file.Stat() if err != nil { @@ -143,7 +142,9 @@ func (d *assetHandler) serveFSFile(rw http.ResponseWriter, req *http.Request, fi if err != nil { return err } - defer file.Close() + defer func() { + _ = file.Close() + }() statInfo, err = file.Stat() if err != nil { diff --git a/v3/examples/dialogs-basic/.hidden_file b/v3/examples/dialogs-basic/.hidden_file new file mode 100644 index 000000000..e69de29bb diff --git a/v3/examples/dialogs-basic/README.md b/v3/examples/dialogs-basic/README.md new file mode 100644 index 000000000..c03911376 --- /dev/null +++ b/v3/examples/dialogs-basic/README.md @@ -0,0 +1,36 @@ +# Dialog Test Application + +This application is designed to test macOS file dialog functionality across different versions of macOS. It provides a comprehensive suite of tests for various dialog features and configurations. + +## Features Tested + +1. Basic file open dialog +2. Single extension filter +3. Multiple extension filter +4. Multiple file selection +5. Directory selection +6. Save dialog with extension +7. Complex filters +8. Hidden files +9. Default directory +10. Full featured dialog with all options + +## Running the Tests + +```bash +go run main.go +``` + +## Test Results + +When running tests: +- Each test will show the selected file(s) and their types +- For multiple selections, all selected files will be listed +- Errors will be displayed in an error dialog +- The application logs debug information to help track issues + +## Notes + +- This test application is primarily for development and testing purposes +- It can be used to verify dialog behavior across different macOS versions +- The tests are designed to not interfere with CI pipelines diff --git a/v3/examples/dialogs-basic/main.go b/v3/examples/dialogs-basic/main.go new file mode 100644 index 000000000..2618e5960 --- /dev/null +++ b/v3/examples/dialogs-basic/main.go @@ -0,0 +1,260 @@ +package main + +import ( + "fmt" + "log" + "log/slog" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/wailsapp/wails/v3/pkg/application" +) + +func main() { + app := application.New(application.Options{ + Name: "Dialog Test", + Description: "Test application for macOS dialogs", + Logger: application.DefaultLogger(slog.LevelDebug), + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + // Create main window + mainWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + Title: "Dialog Tests", + Width: 800, + Height: 600, + MinWidth: 800, + MinHeight: 600, + }) + mainWindow.SetAlwaysOnTop(true) + + // Create main menu + menu := app.NewMenu() + app.SetMenu(menu) + menu.AddRole(application.AppMenu) + menu.AddRole(application.EditMenu) + menu.AddRole(application.WindowMenu) + + // Add test menu + testMenu := menu.AddSubmenu("Tests") + + // Test 1: Basic file open with no filters (no window) + testMenu.Add("1. Basic Open (No Window)").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + PromptForSingleSelection() + showResult("Basic Open", result, err, nil) + }) + + // Test 1b: Basic file open with window + testMenu.Add("1b. Basic Open (With Window)").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Basic Open", result, err, mainWindow) + }) + + // Test 2: Open with single extension filter + testMenu.Add("2. Single Filter").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("Text Files", "*.txt"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Single Filter", result, err, mainWindow) + }) + + // Test 3: Open with multiple extension filter + testMenu.Add("3. Multiple Filter").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("Documents", "*.txt;*.md;*.doc;*.docx"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Multiple Filter", result, err, mainWindow) + }) + + // Test 4: Multiple file selection + testMenu.Add("4. Multiple Selection").OnClick(func(ctx *application.Context) { + results, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("Images", "*.png;*.jpg;*.jpeg"). + AttachToWindow(mainWindow). + PromptForMultipleSelection() + if err != nil { + showError("Multiple Selection", err, mainWindow) + return + } + showResults("Multiple Selection", results, mainWindow) + }) + + // Test 5: Directory selection + testMenu.Add("5. Directory Selection").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseDirectories(true). + CanChooseFiles(false). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Directory Selection", result, err, mainWindow) + }) + + // Test 6: Save dialog with extension + testMenu.Add("6. Save Dialog").OnClick(func(ctx *application.Context) { + result, err := application.SaveFileDialog(). + SetFilename("test.txt"). + AddFilter("Text Files", "*.txt"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Save Dialog", result, err, mainWindow) + }) + + // Test 7: Complex filters + testMenu.Add("7. Complex Filters").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + AddFilter("All Documents", "*.txt;*.md;*.doc;*.docx;*.pdf"). + AddFilter("Text Files", "*.txt"). + AddFilter("Markdown", "*.md"). + AddFilter("Word Documents", "*.doc;*.docx"). + AddFilter("PDF Files", "*.pdf"). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Complex Filters", result, err, mainWindow) + }) + + // Test 8: Hidden files + testMenu.Add("8. Show Hidden").OnClick(func(ctx *application.Context) { + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + ShowHiddenFiles(true). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Show Hidden", result, err, mainWindow) + }) + + // Test 9: Default directory + testMenu.Add("9. Default Directory").OnClick(func(ctx *application.Context) { + home, _ := os.UserHomeDir() + result, err := application.OpenFileDialog(). + CanChooseFiles(true). + SetDirectory(home). + AttachToWindow(mainWindow). + PromptForSingleSelection() + showResult("Default Directory", result, err, mainWindow) + }) + + // Test 10: Full featured dialog + testMenu.Add("10. Full Featured").OnClick(func(ctx *application.Context) { + home, _ := os.UserHomeDir() + dialog := application.OpenFileDialog(). + SetTitle("Full Featured Dialog"). + SetDirectory(home). + CanChooseFiles(true). + CanCreateDirectories(true). + ShowHiddenFiles(true). + ResolvesAliases(true). + AllowsOtherFileTypes(true). + AttachToWindow(mainWindow) + + if runtime.GOOS == "darwin" { + dialog.SetMessage("Please select files") + } + + dialog.AddFilter("All Supported", "*.txt;*.md;*.pdf;*.png;*.jpg") + dialog.AddFilter("Documents", "*.txt;*.md;*.pdf") + dialog.AddFilter("Images", "*.png;*.jpg;*.jpeg") + + results, err := dialog.PromptForMultipleSelection() + if err != nil { + showError("Full Featured", err, mainWindow) + return + } + showResults("Full Featured", results, mainWindow) + }) + + // Show the window + mainWindow.Show() + + // Run the app + if err := app.Run(); err != nil { + log.Fatal(err) + } +} + +func showResult(test string, result string, err error, window *application.WebviewWindow) { + if err != nil { + showError(test, err, window) + return + } + if result == "" { + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage("No file selected") + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() + return + } + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage(fmt.Sprintf("Selected: %s\nType: %s", result, getFileType(result))) + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() +} + +func showResults(test string, results []string, window *application.WebviewWindow) { + if len(results) == 0 { + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage("No files selected") + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() + return + } + var message strings.Builder + message.WriteString(fmt.Sprintf("Selected %d files:\n\n", len(results))) + for _, result := range results { + message.WriteString(fmt.Sprintf("%s (%s)\n", result, getFileType(result))) + } + dialog := application.InfoDialog(). + SetTitle(test). + SetMessage(message.String()) + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() +} + +func showError(test string, err error, window *application.WebviewWindow) { + dialog := application.ErrorDialog(). + SetTitle(test). + SetMessage(fmt.Sprintf("Error: %v", err)) + if window != nil { + dialog.AttachToWindow(window) + } + dialog.Show() +} + +func getFileType(path string) string { + if path == "" { + return "unknown" + } + ext := strings.ToLower(filepath.Ext(path)) + if ext == "" { + if fi, err := os.Stat(path); err == nil && fi.IsDir() { + return "directory" + } + return "no extension" + } + return ext +} diff --git a/v3/examples/dialogs-basic/test.txt b/v3/examples/dialogs-basic/test.txt new file mode 100644 index 000000000..64b89ccaf --- /dev/null +++ b/v3/examples/dialogs-basic/test.txt @@ -0,0 +1 @@ +This is a sample text file to test filtering. \ No newline at end of file diff --git a/v3/examples/dialogs-basic/wails-logo-small.jpg b/v3/examples/dialogs-basic/wails-logo-small.jpg new file mode 100644 index 0000000000000000000000000000000000000000..29cb1129e9fe3d1ed5943713d9e9fc9c4b0d54eb GIT binary patch literal 2662 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 2 { + lang = language[:2] + } + // Now we can read the index.html file in the format + // index..html. + + indexFile, err := defaults.Open("index." + lang + ".html") + if err != nil { + return result + } + + indexBytes, err := io.ReadAll(indexFile) + if err != nil { + return result + } + return indexBytes +} + func (a *AssetServer) LogDetails() { var info = []any{ "middleware", a.options.Middleware != nil, diff --git a/v3/internal/assetserver/assetserver_production.go b/v3/internal/assetserver/assetserver_production.go index 6a7731191..f698fab40 100644 --- a/v3/internal/assetserver/assetserver_production.go +++ b/v3/internal/assetserver/assetserver_production.go @@ -2,4 +2,8 @@ package assetserver +func defaultIndexHTML(_ string) []byte { + return []byte("index.html not found") +} + func (a *AssetServer) LogDetails() {} diff --git a/v3/internal/assetserver/build_dev.go b/v3/internal/assetserver/build_dev.go index dda03f125..7747a7142 100644 --- a/v3/internal/assetserver/build_dev.go +++ b/v3/internal/assetserver/build_dev.go @@ -11,13 +11,6 @@ import ( "os" ) -//go:embed defaultindex.html -var defaultHTML []byte - -func defaultIndexHTML() []byte { - return defaultHTML -} - func NewAssetFileServer(vfs fs.FS) http.Handler { devServerURL := GetDevServerURL() if devServerURL == "" { diff --git a/v3/internal/assetserver/defaultindex.html b/v3/internal/assetserver/defaultindex.html deleted file mode 100644 index 1ea97c405..000000000 --- a/v3/internal/assetserver/defaultindex.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - index.html not found - - - - -
index.html not found
-

Please try reloading the page

- - \ No newline at end of file diff --git a/v3/internal/assetserver/defaults/index.en.html b/v3/internal/assetserver/defaults/index.en.html new file mode 100644 index 000000000..4ecfa9ea7 --- /dev/null +++ b/v3/internal/assetserver/defaults/index.en.html @@ -0,0 +1,350 @@ + + + + + + Page Not Found - Wails + + + +
+ +
+ +
+ + +
+
+
+
⚠️
+ Missing index.html file +
+
+

No index.html file was found in the embedded assets. This page appears when the WebView window cannot find HTML content to display.

+
    +
  • + 1 + + If you are using the Assets option in your application, ensure you have an index.html file in your project's embedded assets directory. +
    + View Example → +
    +//go:embed all:frontend/dist +var assets embed.FS + +func main() { + // ... + app := application.New(application.Options{ + // ... + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + }) + // ... +} +
    +
    +
    +
  • +
  • + 2 + If the file doesn't exist but should, verify that your build process is configured to correctly include the HTML file in the embedded assets directory. +
  • +
  • + 3 + + An alternative solution is to use the HTML option in the WebviewWindow Options. +
    + View Example → +
    +func main() { + + // ... + + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + // ... + HTML: "<h1>Hello World!<h1>", + }) + // ... +} +
    +
    +
    +
  • +
+
+ +
+ + +
+
+
+ + + +
+ + \ No newline at end of file diff --git a/v3/internal/assetserver/defaults/index.zh.html b/v3/internal/assetserver/defaults/index.zh.html new file mode 100644 index 000000000..7aeefcd49 --- /dev/null +++ b/v3/internal/assetserver/defaults/index.zh.html @@ -0,0 +1,302 @@ + + + + + + 页面未找到 - Wails + + + +
+ +
+ +
+ + +
+
+
+
+ 未找到 index.html 文件 +
请按照以下步骤解决此问题
+
+
+

+ 系统提示:在嵌入资源中未能找到 index.html 文件。 +
+ 不用担心,这个问题很容易解决。 +

+
    +
  • + 1 + + 如果您在应用程序中使用了 Assets 选项,请确保您的项目嵌入资源目录中有 index.html 文件。 +
    + 查看示例 ➜ +
    +//go:embed all:frontend/dist +var assets embed.FS + +func main() { + // ... + app := application.New(application.Options{ + // ... + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + }) + // ... +} +
    +
    +
    +
  • +
  • + 2 + 如果文件应该存在但不存在,请验证您的构建过程是否配置正确,以确保 HTML 文件包含在嵌入资源目录中。 +
  • +
  • + 3 + + 另一种解决方案是在 WebviewWindow 选项中使用 HTML 选项。 +
    + 查看示例 ➜ +
    +func main() { + + // ... + + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + // ... + HTML: "<h1>Hello World!<h1>", + }) + // ... +} +
    +
    +
    +
  • +
+
+ +
+ + +
+
+
+ + +
+

需要帮助?查看我们的文档或加入我们的社区

+
+
+ + diff --git a/v3/pkg/application/dialogs_darwin_delegate.h b/v3/pkg/application/dialogs_darwin_delegate.h index 07657f8b9..d1c732a91 100644 --- a/v3/pkg/application/dialogs_darwin_delegate.h +++ b/v3/pkg/application/dialogs_darwin_delegate.h @@ -3,10 +3,14 @@ #ifndef _DIALOGS_DELEGATE_H_ #define _DIALOGS_DELEGATE_H_ -#import #import -// create an NSOpenPanel delegate to handle the callback +// Conditionally import UniformTypeIdentifiers based on OS version +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 +#import +#endif + +// OpenPanel delegate to handle file filtering @interface OpenPanelDelegate : NSObject @property (nonatomic, strong) NSArray *allowedExtensions; @end diff --git a/v3/pkg/application/dialogs_darwin_delegate.m b/v3/pkg/application/dialogs_darwin_delegate.m index 5cbd46a2b..284f98ab8 100644 --- a/v3/pkg/application/dialogs_darwin_delegate.m +++ b/v3/pkg/application/dialogs_darwin_delegate.m @@ -8,29 +8,31 @@ if (url == nil) { return NO; } + NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDirectory = NO; if ([fileManager fileExistsAtPath:url.path isDirectory:&isDirectory] && isDirectory) { return YES; } - if (self.allowedExtensions == nil) { + + // If no extensions specified, allow all files + if (self.allowedExtensions == nil || [self.allowedExtensions count] == 0) { return YES; } - NSString *extension = url.pathExtension; - if (extension == nil) { + + NSString *extension = [url.pathExtension lowercaseString]; + if (extension == nil || [extension isEqualToString:@""]) { return NO; } - if ([extension isEqualToString:@""]) { - return NO; - } - if ([self.allowedExtensions containsObject:extension]) { - return YES; + + // Check if the extension is in our allowed list (case insensitive) + for (NSString *allowedExt in self.allowedExtensions) { + if ([[allowedExt lowercaseString] isEqualToString:extension]) { + return YES; + } } + return NO; } @end - - - -