mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 22:55:48 +01:00
* feat(v3): add server mode for headless HTTP deployment Server mode allows Wails applications to run as pure HTTP servers without native GUI dependencies. Enable with `-tags server` build tag. Features: - HTTP server with configurable host/port via ServerOptions - WAILS_SERVER_HOST and WAILS_SERVER_PORT env var overrides - WebSocket event broadcasting to connected browsers - Browser clients represented as BrowserWindow (Window interface) - Health check endpoint at /health - Graceful shutdown with configurable timeout - Docker support with Dockerfile.server template and tasks Build and run: wails3 task build:server wails3 task run:server wails3 task build:docker wails3 task run:docker Documentation at docs/guides/server-build.mdx Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(v3): add server mode for headless HTTP deployment Server mode allows Wails applications to run as pure HTTP servers without native GUI dependencies. Enable with `-tags server` build tag. Features: - HTTP server with configurable host/port via ServerOptions - WAILS_SERVER_HOST and WAILS_SERVER_PORT env var overrides - WebSocket event broadcasting to connected browsers - Browser clients represented as BrowserWindow (Window interface) - Health check endpoint at /health - Graceful shutdown with configurable timeout - Docker support with Dockerfile.server template and tasks Build and run: wails3 task build:server wails3 task run:server wails3 task build:docker wails3 task run:docker Documentation at docs/guides/server-build.mdx Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review comments - Fix corrupted test file with embedded terminal output - Fix module name mismatch in gin-routing (was gin-example) - Fix replace directive version mismatch in gin-service - Fix placeholder module name in ios example (was changeme) - Fix Dockerfile COPY path to work from both build contexts - Fix bare URL in README (MD034 compliance) - Fix comment accuracy in getScreens (returns error, not empty slice) - Remove deprecated docker-compose version field - Add port documentation in Taskfile template Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: address CodeRabbit review comments - Add note about healthcheck wget not being available in distroless images - Add !server build constraint to menu_windows.go and menu_darwin.go - Downgrade window-visibility-test go.mod from 1.25 to 1.24 to match CI Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
554 lines
19 KiB
Go
554 lines
19 KiB
Go
//go:build server
|
|
|
|
package application
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"syscall"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
// serverApp implements platformApp for server mode.
|
|
// It provides a minimal implementation that runs an HTTP server
|
|
// without any GUI components.
|
|
//
|
|
// Server mode is enabled by building with the "server" build tag:
|
|
//
|
|
// go build -tags server
|
|
type serverApp struct {
|
|
app *App
|
|
server *http.Server
|
|
listener net.Listener
|
|
broadcaster *WebSocketBroadcaster
|
|
}
|
|
|
|
// newPlatformApp creates a new server-mode platform app.
|
|
// This function is only compiled when building with the "server" tag.
|
|
func newPlatformApp(app *App) *serverApp {
|
|
app.info("Server mode enabled (built with -tags server)")
|
|
return &serverApp{
|
|
app: app,
|
|
}
|
|
}
|
|
|
|
// parsePort parses a port string into an integer.
|
|
func parsePort(s string) (int, error) {
|
|
p, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if p < 1 || p > 65535 {
|
|
return 0, errors.New("port out of range")
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
// run starts the HTTP server and blocks until shutdown.
|
|
func (h *serverApp) run() error {
|
|
// Set up common events
|
|
h.setupCommonEvents()
|
|
|
|
// Create WebSocket broadcaster for events
|
|
h.broadcaster = NewWebSocketBroadcaster(h.app)
|
|
globalBroadcaster = h.broadcaster // Set global reference for browser ID lookups
|
|
h.app.wailsEventListenerLock.Lock()
|
|
h.app.wailsEventListeners = append(h.app.wailsEventListeners, h.broadcaster)
|
|
h.app.wailsEventListenerLock.Unlock()
|
|
|
|
opts := h.app.options.Server
|
|
|
|
// Environment variables override config (useful for Docker/containers)
|
|
host := os.Getenv("WAILS_SERVER_HOST")
|
|
if host == "" {
|
|
host = opts.Host
|
|
}
|
|
if host == "" {
|
|
host = "localhost"
|
|
}
|
|
|
|
port := opts.Port
|
|
if envPort := os.Getenv("WAILS_SERVER_PORT"); envPort != "" {
|
|
if p, err := parsePort(envPort); err == nil {
|
|
port = p
|
|
}
|
|
}
|
|
if port == 0 {
|
|
port = 8080
|
|
}
|
|
|
|
readTimeout := opts.ReadTimeout
|
|
if readTimeout == 0 {
|
|
readTimeout = 30 * time.Second
|
|
}
|
|
|
|
writeTimeout := opts.WriteTimeout
|
|
if writeTimeout == 0 {
|
|
writeTimeout = 30 * time.Second
|
|
}
|
|
|
|
idleTimeout := opts.IdleTimeout
|
|
if idleTimeout == 0 {
|
|
idleTimeout = 120 * time.Second
|
|
}
|
|
|
|
shutdownTimeout := opts.ShutdownTimeout
|
|
if shutdownTimeout == 0 {
|
|
shutdownTimeout = 30 * time.Second
|
|
}
|
|
|
|
addr := fmt.Sprintf("%s:%d", host, port)
|
|
|
|
// Create HTTP handler from asset server
|
|
handler := h.createHandler()
|
|
|
|
h.server = &http.Server{
|
|
Addr: addr,
|
|
Handler: handler,
|
|
ReadTimeout: readTimeout,
|
|
WriteTimeout: writeTimeout,
|
|
IdleTimeout: idleTimeout,
|
|
}
|
|
|
|
// Create listener
|
|
var err error
|
|
h.listener, err = net.Listen("tcp", addr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to listen on %s: %w", addr, err)
|
|
}
|
|
|
|
h.app.info("Server mode starting", "address", addr)
|
|
|
|
// Start server in goroutine
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
if opts.TLS != nil {
|
|
errCh <- h.server.ServeTLS(h.listener, opts.TLS.CertFile, opts.TLS.KeyFile)
|
|
} else {
|
|
errCh <- h.server.Serve(h.listener)
|
|
}
|
|
}()
|
|
|
|
// Wait for shutdown signal or error
|
|
quit := make(chan os.Signal, 1)
|
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
select {
|
|
case err := <-errCh:
|
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
return err
|
|
}
|
|
case <-quit:
|
|
h.app.info("Shutdown signal received")
|
|
case <-h.app.ctx.Done():
|
|
h.app.info("Application context cancelled")
|
|
}
|
|
|
|
// Graceful shutdown
|
|
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
|
defer cancel()
|
|
|
|
if err := h.server.Shutdown(ctx); err != nil {
|
|
return fmt.Errorf("server shutdown error: %w", err)
|
|
}
|
|
|
|
h.app.info("Server stopped")
|
|
return nil
|
|
}
|
|
|
|
// customJS is the JavaScript that sets up WebSocket event connection for server mode.
|
|
// Events FROM frontend TO backend use the existing HTTP transport.
|
|
// This WebSocket is only for receiving broadcast events FROM backend TO all frontends.
|
|
const customJS = `(function() {
|
|
var protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
var clientId = window._wails && window._wails.clientId ? window._wails.clientId : '';
|
|
var wsUrl = protocol + '//' + location.host + '/wails/events' + (clientId ? '?clientId=' + encodeURIComponent(clientId) : '');
|
|
var ws;
|
|
|
|
function connect() {
|
|
ws = new WebSocket(wsUrl);
|
|
ws.onopen = function() {
|
|
console.log('[Wails] Event WebSocket connected');
|
|
};
|
|
ws.onmessage = function(e) {
|
|
try {
|
|
var event = JSON.parse(e.data);
|
|
if (window._wails && window._wails.dispatchWailsEvent) {
|
|
window._wails.dispatchWailsEvent(event);
|
|
}
|
|
} catch (err) {
|
|
console.error('[Wails] Failed to parse event:', err);
|
|
}
|
|
};
|
|
ws.onclose = function() {
|
|
console.log('[Wails] Event WebSocket disconnected, reconnecting...');
|
|
setTimeout(connect, 1000);
|
|
};
|
|
ws.onerror = function() {
|
|
ws.close();
|
|
};
|
|
}
|
|
|
|
connect();
|
|
})();`
|
|
|
|
// createHandler creates the HTTP handler for server mode.
|
|
func (h *serverApp) createHandler() http.Handler {
|
|
mux := http.NewServeMux()
|
|
|
|
// Health check endpoint
|
|
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(`{"status":"ok"}`))
|
|
})
|
|
|
|
// Serve custom.js for server mode (WebSocket event connection)
|
|
mux.HandleFunc("/wails/custom.js", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/javascript")
|
|
w.WriteHeader(http.StatusOK)
|
|
w.Write([]byte(customJS))
|
|
})
|
|
|
|
// WebSocket endpoint for events
|
|
mux.Handle("/wails/events", h.broadcaster)
|
|
|
|
// Serve all other requests through the asset server
|
|
mux.Handle("/", h.app.assets)
|
|
|
|
return mux
|
|
}
|
|
|
|
// destroy stops the server and cleans up.
|
|
func (h *serverApp) destroy() {
|
|
if h.server != nil {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
h.server.Shutdown(ctx)
|
|
}
|
|
h.app.cleanup()
|
|
}
|
|
|
|
// setApplicationMenu is a no-op in server mode.
|
|
func (h *serverApp) setApplicationMenu(menu *Menu) {
|
|
// No-op: server mode has no GUI
|
|
}
|
|
|
|
// name returns the application name.
|
|
func (h *serverApp) name() string {
|
|
return h.app.options.Name
|
|
}
|
|
|
|
// getCurrentWindowID returns 0 in server mode (no windows).
|
|
func (h *serverApp) getCurrentWindowID() uint {
|
|
return 0
|
|
}
|
|
|
|
// showAboutDialog is a no-op in server mode.
|
|
func (h *serverApp) showAboutDialog(name string, description string, icon []byte) {
|
|
// No-op: server mode has no GUI
|
|
h.app.warning("showAboutDialog called in server mode - operation ignored")
|
|
}
|
|
|
|
// setIcon is a no-op in server mode.
|
|
func (h *serverApp) setIcon(icon []byte) {
|
|
// No-op: server mode has no GUI
|
|
}
|
|
|
|
// on is a no-op in server mode.
|
|
func (h *serverApp) on(id uint) {
|
|
// No-op: server mode has no platform-specific event handling
|
|
}
|
|
|
|
// dispatchOnMainThread executes the function directly in server mode.
|
|
func (h *serverApp) dispatchOnMainThread(id uint) {
|
|
// In server mode, there's no "main thread" concept from GUI frameworks
|
|
// Execute the function directly
|
|
mainThreadFunctionStoreLock.Lock()
|
|
fn, ok := mainThreadFunctionStore[id]
|
|
if ok {
|
|
delete(mainThreadFunctionStore, id)
|
|
}
|
|
mainThreadFunctionStoreLock.Unlock()
|
|
|
|
if ok && fn != nil {
|
|
fn()
|
|
}
|
|
}
|
|
|
|
// hide is a no-op in server mode.
|
|
func (h *serverApp) hide() {
|
|
// No-op: server mode has no GUI
|
|
}
|
|
|
|
// show is a no-op in server mode.
|
|
func (h *serverApp) show() {
|
|
// No-op: server mode has no GUI
|
|
}
|
|
|
|
// getPrimaryScreen returns nil in server mode.
|
|
func (h *serverApp) getPrimaryScreen() (*Screen, error) {
|
|
return nil, errors.New("screen information not available in server mode")
|
|
}
|
|
|
|
// getScreens returns an error in server mode (screen info unavailable).
|
|
func (h *serverApp) getScreens() ([]*Screen, error) {
|
|
return nil, errors.New("screen information not available in server mode")
|
|
}
|
|
|
|
// GetFlags returns the application flags for server mode.
|
|
func (h *serverApp) GetFlags(options Options) map[string]any {
|
|
flags := make(map[string]any)
|
|
flags["server"] = true
|
|
if options.Flags != nil {
|
|
for k, v := range options.Flags {
|
|
flags[k] = v
|
|
}
|
|
}
|
|
return flags
|
|
}
|
|
|
|
// isOnMainThread always returns true in server mode.
|
|
func (h *serverApp) isOnMainThread() bool {
|
|
// In server mode, there's no main thread concept
|
|
return true
|
|
}
|
|
|
|
// isDarkMode returns false in server mode.
|
|
func (h *serverApp) isDarkMode() bool {
|
|
return false
|
|
}
|
|
|
|
// getAccentColor returns empty string in server mode.
|
|
func (h *serverApp) getAccentColor() string {
|
|
return ""
|
|
}
|
|
|
|
// logPlatformInfo logs platform info for server mode.
|
|
func (a *App) logPlatformInfo() {
|
|
a.info("Platform Info:", "mode", "server")
|
|
}
|
|
|
|
// platformEnvironment returns environment info for server mode.
|
|
func (a *App) platformEnvironment() map[string]any {
|
|
return map[string]any{
|
|
"mode": "server",
|
|
}
|
|
}
|
|
|
|
// fatalHandler sets up fatal error handling for server mode.
|
|
func fatalHandler(errFunc func(error)) {
|
|
// In server mode, fatal errors are handled via standard mechanisms
|
|
}
|
|
|
|
// newClipboardImpl creates a clipboard implementation for server mode.
|
|
func newClipboardImpl() clipboardImpl {
|
|
return &serverClipboard{}
|
|
}
|
|
|
|
// serverClipboard is a no-op clipboard for server mode.
|
|
type serverClipboard struct{}
|
|
|
|
func (c *serverClipboard) setText(text string) bool {
|
|
return false
|
|
}
|
|
|
|
func (c *serverClipboard) text() (string, bool) {
|
|
return "", false
|
|
}
|
|
|
|
// newDialogImpl creates a dialog implementation for server mode.
|
|
func newDialogImpl(d *MessageDialog) messageDialogImpl {
|
|
return &serverDialog{}
|
|
}
|
|
|
|
// serverDialog is a no-op dialog for server mode.
|
|
type serverDialog struct{}
|
|
|
|
func (d *serverDialog) show() {
|
|
// No-op in server mode
|
|
}
|
|
|
|
// newOpenFileDialogImpl creates an open file dialog implementation for server mode.
|
|
func newOpenFileDialogImpl(d *OpenFileDialogStruct) openFileDialogImpl {
|
|
return &serverOpenFileDialog{}
|
|
}
|
|
|
|
// serverOpenFileDialog is a no-op open file dialog for server mode.
|
|
type serverOpenFileDialog struct{}
|
|
|
|
func (d *serverOpenFileDialog) show() (chan string, error) {
|
|
ch := make(chan string, 1)
|
|
close(ch)
|
|
return ch, errors.New("file dialogs not available in server mode")
|
|
}
|
|
|
|
// newSaveFileDialogImpl creates a save file dialog implementation for server mode.
|
|
func newSaveFileDialogImpl(d *SaveFileDialogStruct) saveFileDialogImpl {
|
|
return &serverSaveFileDialog{}
|
|
}
|
|
|
|
// serverSaveFileDialog is a no-op save file dialog for server mode.
|
|
type serverSaveFileDialog struct{}
|
|
|
|
func (d *serverSaveFileDialog) show() (chan string, error) {
|
|
ch := make(chan string, 1)
|
|
close(ch)
|
|
return ch, errors.New("file dialogs not available in server mode")
|
|
}
|
|
|
|
// newMenuImpl creates a menu implementation for server mode.
|
|
func newMenuImpl(menu *Menu) menuImpl {
|
|
return &serverMenu{}
|
|
}
|
|
|
|
// serverMenu is a no-op menu for server mode.
|
|
type serverMenu struct{}
|
|
|
|
func (m *serverMenu) update() {
|
|
// No-op in server mode
|
|
}
|
|
|
|
// newPlatformLock creates a platform-specific single instance lock for server mode.
|
|
func newPlatformLock(manager *singleInstanceManager) (platformLock, error) {
|
|
return &serverLock{}, nil
|
|
}
|
|
|
|
// serverLock is a basic lock for server mode.
|
|
type serverLock struct{}
|
|
|
|
func (l *serverLock) acquire(uniqueID string) error {
|
|
return nil
|
|
}
|
|
|
|
func (l *serverLock) release() {
|
|
// No-op in server mode
|
|
}
|
|
|
|
func (l *serverLock) notify(data string) error {
|
|
return errors.New("single instance not supported in server mode")
|
|
}
|
|
|
|
// newSystemTrayImpl creates a system tray implementation for server mode.
|
|
func newSystemTrayImpl(s *SystemTray) systemTrayImpl {
|
|
return &serverSystemTray{parent: s}
|
|
}
|
|
|
|
// serverSystemTray is a no-op system tray for server mode.
|
|
type serverSystemTray struct {
|
|
parent *SystemTray
|
|
}
|
|
|
|
func (t *serverSystemTray) setLabel(label string) {}
|
|
func (t *serverSystemTray) setTooltip(tooltip string) {}
|
|
func (t *serverSystemTray) run() {}
|
|
func (t *serverSystemTray) setIcon(icon []byte) {}
|
|
func (t *serverSystemTray) setMenu(menu *Menu) {}
|
|
func (t *serverSystemTray) setIconPosition(pos IconPosition) {}
|
|
func (t *serverSystemTray) setTemplateIcon(icon []byte) {}
|
|
func (t *serverSystemTray) destroy() {}
|
|
func (t *serverSystemTray) setDarkModeIcon(icon []byte) {}
|
|
func (t *serverSystemTray) bounds() (*Rect, error) { return nil, errors.New("system tray not available in server mode") }
|
|
func (t *serverSystemTray) getScreen() (*Screen, error) { return nil, errors.New("system tray not available in server mode") }
|
|
func (t *serverSystemTray) positionWindow(w Window, o int) error { return errors.New("system tray not available in server mode") }
|
|
func (t *serverSystemTray) openMenu() {}
|
|
func (t *serverSystemTray) Show() {}
|
|
func (t *serverSystemTray) Hide() {}
|
|
|
|
// newWindowImpl creates a webview window implementation for server mode.
|
|
func newWindowImpl(parent *WebviewWindow) *serverWebviewWindow {
|
|
return &serverWebviewWindow{parent: parent}
|
|
}
|
|
|
|
// serverWebviewWindow is a no-op webview window for server mode.
|
|
type serverWebviewWindow struct {
|
|
parent *WebviewWindow
|
|
}
|
|
|
|
// All webviewWindowImpl methods as no-ops for server mode
|
|
func (w *serverWebviewWindow) setTitle(title string) {}
|
|
func (w *serverWebviewWindow) setSize(width, height int) {}
|
|
func (w *serverWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) {}
|
|
func (w *serverWebviewWindow) setURL(url string) {}
|
|
func (w *serverWebviewWindow) setResizable(resizable bool) {}
|
|
func (w *serverWebviewWindow) setMinSize(width, height int) {}
|
|
func (w *serverWebviewWindow) setMaxSize(width, height int) {}
|
|
func (w *serverWebviewWindow) execJS(js string) {}
|
|
func (w *serverWebviewWindow) setBackgroundColour(color RGBA) {}
|
|
func (w *serverWebviewWindow) run() {}
|
|
func (w *serverWebviewWindow) center() {}
|
|
func (w *serverWebviewWindow) size() (int, int) { return 0, 0 }
|
|
func (w *serverWebviewWindow) width() int { return 0 }
|
|
func (w *serverWebviewWindow) height() int { return 0 }
|
|
func (w *serverWebviewWindow) destroy() {}
|
|
func (w *serverWebviewWindow) reload() {}
|
|
func (w *serverWebviewWindow) forceReload() {}
|
|
func (w *serverWebviewWindow) openDevTools() {}
|
|
func (w *serverWebviewWindow) zoomReset() {}
|
|
func (w *serverWebviewWindow) zoomIn() {}
|
|
func (w *serverWebviewWindow) zoomOut() {}
|
|
func (w *serverWebviewWindow) getZoom() float64 { return 1.0 }
|
|
func (w *serverWebviewWindow) setZoom(zoom float64) {}
|
|
func (w *serverWebviewWindow) close() {}
|
|
func (w *serverWebviewWindow) zoom() {}
|
|
func (w *serverWebviewWindow) setHTML(html string) {}
|
|
func (w *serverWebviewWindow) on(eventID uint) {}
|
|
func (w *serverWebviewWindow) minimise() {}
|
|
func (w *serverWebviewWindow) unminimise() {}
|
|
func (w *serverWebviewWindow) maximise() {}
|
|
func (w *serverWebviewWindow) unmaximise() {}
|
|
func (w *serverWebviewWindow) fullscreen() {}
|
|
func (w *serverWebviewWindow) unfullscreen() {}
|
|
func (w *serverWebviewWindow) isMinimised() bool { return false }
|
|
func (w *serverWebviewWindow) isMaximised() bool { return false }
|
|
func (w *serverWebviewWindow) isFullscreen() bool { return false }
|
|
func (w *serverWebviewWindow) isNormal() bool { return true }
|
|
func (w *serverWebviewWindow) isVisible() bool { return false }
|
|
func (w *serverWebviewWindow) isFocused() bool { return false }
|
|
func (w *serverWebviewWindow) focus() {}
|
|
func (w *serverWebviewWindow) show() {}
|
|
func (w *serverWebviewWindow) hide() {}
|
|
func (w *serverWebviewWindow) getScreen() (*Screen, error) { return nil, errors.New("screens not available in server mode") }
|
|
func (w *serverWebviewWindow) setFrameless(frameless bool) {}
|
|
func (w *serverWebviewWindow) openContextMenu(menu *Menu, data *ContextMenuData) {}
|
|
func (w *serverWebviewWindow) nativeWindow() unsafe.Pointer { return nil }
|
|
func (w *serverWebviewWindow) startDrag() error { return errors.New("drag not available in server mode") }
|
|
func (w *serverWebviewWindow) startResize(border string) error { return errors.New("resize not available in server mode") }
|
|
func (w *serverWebviewWindow) print() error { return errors.New("print not available in server mode") }
|
|
func (w *serverWebviewWindow) setEnabled(enabled bool) {}
|
|
func (w *serverWebviewWindow) physicalBounds() Rect { return Rect{} }
|
|
func (w *serverWebviewWindow) setPhysicalBounds(bounds Rect) {}
|
|
func (w *serverWebviewWindow) bounds() Rect { return Rect{} }
|
|
func (w *serverWebviewWindow) setBounds(bounds Rect) {}
|
|
func (w *serverWebviewWindow) position() (int, int) { return 0, 0 }
|
|
func (w *serverWebviewWindow) setPosition(x int, y int) {}
|
|
func (w *serverWebviewWindow) relativePosition() (int, int) { return 0, 0 }
|
|
func (w *serverWebviewWindow) setRelativePosition(x int, y int) {}
|
|
func (w *serverWebviewWindow) flash(enabled bool) {}
|
|
func (w *serverWebviewWindow) handleKeyEvent(acceleratorString string) {}
|
|
func (w *serverWebviewWindow) getBorderSizes() *LRTB { return &LRTB{} }
|
|
func (w *serverWebviewWindow) setMinimiseButtonState(state ButtonState) {}
|
|
func (w *serverWebviewWindow) setMaximiseButtonState(state ButtonState) {}
|
|
func (w *serverWebviewWindow) setCloseButtonState(state ButtonState) {}
|
|
func (w *serverWebviewWindow) isIgnoreMouseEvents() bool { return false }
|
|
func (w *serverWebviewWindow) setIgnoreMouseEvents(ignore bool) {}
|
|
func (w *serverWebviewWindow) cut() {}
|
|
func (w *serverWebviewWindow) copy() {}
|
|
func (w *serverWebviewWindow) paste() {}
|
|
func (w *serverWebviewWindow) undo() {}
|
|
func (w *serverWebviewWindow) delete() {}
|
|
func (w *serverWebviewWindow) selectAll() {}
|
|
func (w *serverWebviewWindow) redo() {}
|
|
func (w *serverWebviewWindow) showMenuBar() {}
|
|
func (w *serverWebviewWindow) hideMenuBar() {}
|
|
func (w *serverWebviewWindow) toggleMenuBar() {}
|
|
func (w *serverWebviewWindow) setMenu(menu *Menu) {}
|
|
func (w *serverWebviewWindow) snapAssist() {}
|
|
func (w *serverWebviewWindow) setContentProtection(enabled bool) {}
|