5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 03:21:32 +08:00

Fix default index.html serving.

Support multi-language default.
Remove default page from production builds.
Improve defaultindex.html.
This commit is contained in:
Lea Anthony 2024-12-14 07:50:55 +11:00
parent beacf06c7d
commit a0b2ab7c0a
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
16 changed files with 1025 additions and 66 deletions

View File

@ -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 {

View File

View File

@ -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

View File

@ -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
}

View File

@ -0,0 +1 @@
This is a sample text file to test filtering.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -14,6 +14,7 @@ const (
webViewRequestHeaderWindowId = "x-wails-window-id"
webViewRequestHeaderWindowName = "x-wails-window-name"
servicePrefix = "wails/services"
HeaderAcceptLanguage = "accept-language"
)
type RuntimeHandler interface {
@ -100,7 +101,14 @@ func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userH
a.writeBlob(rw, indexHTML, recorder.Body.Bytes())
case http.StatusNotFound:
a.writeBlob(rw, indexHTML, defaultIndexHTML())
// Read the accept-language header
acceptLanguage := req.Header.Get(HeaderAcceptLanguage)
if acceptLanguage == "" {
acceptLanguage = "en"
}
// Set content type for default index.html
header.Set(HeaderContentType, "text/html; charset=utf-8")
a.writeBlob(rw, indexHTML, defaultIndexHTML(acceptLanguage))
default:
rw.WriteHeader(recorder.Code)

View File

@ -2,6 +2,43 @@
package assetserver
import (
"embed"
_ "embed"
"io"
iofs "io/fs"
)
//go:embed defaults
var defaultHTML embed.FS
func defaultIndexHTML(language string) []byte {
result := []byte("index.html not found")
// Create an fs.Sub in the defaults directory
defaults, err := iofs.Sub(defaultHTML, "defaults")
if err != nil {
return result
}
// Get the 2 character language code
lang := "en"
if len(language) >= 2 {
lang = language[:2]
}
// Now we can read the index.html file in the format
// index.<lang>.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,

View File

@ -2,4 +2,8 @@
package assetserver
func defaultIndexHTML(_ string) []byte {
return []byte("index.html not found")
}
func (a *AssetServer) LogDetails() {}

View File

@ -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 == "" {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,10 +3,14 @@
#ifndef _DIALOGS_DELEGATE_H_
#define _DIALOGS_DELEGATE_H_
#import <UniformTypeIdentifiers/UTType.h>
#import <Cocoa/Cocoa.h>
// create an NSOpenPanel delegate to handle the callback
// Conditionally import UniformTypeIdentifiers based on OS version
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000
#import <UniformTypeIdentifiers/UTType.h>
#endif
// OpenPanel delegate to handle file filtering
@interface OpenPanelDelegate : NSObject <NSOpenSavePanelDelegate>
@property (nonatomic, strong) NSArray *allowedExtensions;
@end

View File

@ -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