diff --git a/.gitignore b/.gitignore index 4c75354..3ef8ede 100644 --- a/.gitignore +++ b/.gitignore @@ -11,10 +11,12 @@ __pycache__/ *.pyc .venv/ -# Wails(构建产物 + 生成的绑定/缓存,每次 wails build 自动重建) +# Wails v3(只忽略产物/缓存;build/ 的 Taskfile+配置是项目构建系统,需提交) build/bin/ frontend/dist/ -sundynix-desktop/build/ +sundynix-desktop/build/bin/ +sundynix-desktop/.task/ +.task/ sundynix-desktop/frontend/wailsjs/ sundynix-desktop/frontend/package.json.md5 diff --git a/sundynix-desktop/Taskfile.yml b/sundynix-desktop/Taskfile.yml new file mode 100644 index 0000000..370f558 --- /dev/null +++ b/sundynix-desktop/Taskfile.yml @@ -0,0 +1,63 @@ +version: '3' + +vars: + APP_NAME: "sundynix_desktop" + BIN_DIR: "bin" + PACKAGE_MANAGER: '{{.PACKAGE_MANAGER | default "npm"}}' + VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}' + # Target OS for build/package/run. Defaults to the host OS, and is overridden + # by `wails3 build GOOS=...` (or the GOOS env var) for cross-compilation. The + # tasks below dispatch to the matching platform Taskfile via this variable. + GOOS: '{{.GOOS | default OS}}' + +includes: + common: ./build/Taskfile.yml + windows: ./build/windows/Taskfile.yml + darwin: ./build/darwin/Taskfile.yml + linux: ./build/linux/Taskfile.yml + +tasks: + build: + summary: Builds the application + cmds: + - task: "{{.GOOS}}:build" + + package: + summary: Packages a production build of the application + cmds: + - task: "{{.GOOS}}:package" + + run: + summary: Runs the application + cmds: + - task: "{{.GOOS}}:run" + + dev: + summary: Runs the application in development mode + cmds: + - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}} + + setup:docker: + summary: Builds Docker image for cross-compilation (~800MB download) + cmds: + - task: common:setup:docker + + build:server: + summary: Builds the application in server mode (no GUI, HTTP server only) + cmds: + - task: common:build:server + + run:server: + summary: Runs the application in server mode + cmds: + - task: common:run:server + + build:docker: + summary: Builds a Docker image for server mode deployment + cmds: + - task: common:build:docker + + run:docker: + summary: Builds and runs the Docker image + cmds: + - task: common:run:docker diff --git a/sundynix-desktop/app.go b/sundynix-desktop/app.go index f4ee277..09dbd3e 100644 --- a/sundynix-desktop/app.go +++ b/sundynix-desktop/app.go @@ -1,7 +1,6 @@ package main import ( - "context" "fmt" "io" "net/http" @@ -10,19 +9,13 @@ import ( "path/filepath" goruntime "runtime" - wr "github.com/wailsapp/wails/v2/pkg/runtime" + "github.com/wailsapp/wails/v3/pkg/application" ) -// App 经 Wails 的 TS/Go 强绑定暴露给前端,承载只有桌面端能做的原生能力: +// App 经 Wails v3 的 Service 绑定暴露给前端,承载只有桌面端能做的原生能力: // 文件读写、系统"另存为"框、用系统默认应用打开、原生通知。 -type App struct { - ctx context.Context -} - -func NewApp() *App { return &App{} } - -// startup 在 Wails 启动时注入运行时 ctx。 -func (a *App) startup(ctx context.Context) { a.ctx = ctx } +// v3 中不再需要注入 ctx——对话框经 application.Get().Dialog 获取。 +type App struct{} // Ping 供前端探活 Go 桥是否就绪。 func (a *App) Ping() string { return "sundynix-desktop ok" } @@ -42,11 +35,10 @@ func (a *App) SaveReportAs(url, filename string) (string, error) { if filename == "" { filename = "report.docx" } - path, err := wr.SaveFileDialog(a.ctx, wr.SaveDialogOptions{ - Title: "保存报告", - DefaultFilename: filename, - Filters: []wr.FileFilter{{DisplayName: "Word 文档 (*.docx)", Pattern: "*.docx"}}, - }) + path, err := application.Get().Dialog.SaveFile(). + SetFilename(filename). + AddFilter("Word 文档 (*.docx)", "*.docx"). + PromptForSingleSelection() if err != nil || path == "" { return "", err } diff --git a/sundynix-desktop/build/Taskfile.yml b/sundynix-desktop/build/Taskfile.yml new file mode 100644 index 0000000..9714b9e --- /dev/null +++ b/sundynix-desktop/build/Taskfile.yml @@ -0,0 +1,398 @@ +version: '3' + +tasks: + go:mod:tidy: + summary: Runs `go mod tidy` + internal: true + # Universal/multi-arch builds invoke this concurrently from parallel deps; + # two `go mod tidy` processes racing on go.mod can corrupt it (#4637). + run: once + cmds: + - go mod tidy + + install:frontend:deps: + summary: Install frontend dependencies + run: once + cmds: + - task: install:frontend:deps:{{.PACKAGE_MANAGER}} + + install:frontend:deps:npm: + dir: frontend + sources: + - package.json + - package-lock.json + generates: + - node_modules + preconditions: + - sh: npm version + msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/" + cmds: + - npm install + + install:frontend:deps:bun: + dir: frontend + sources: + - package.json + - bun.lock + - bun.lockb + generates: + - node_modules + preconditions: + - sh: bun --version + msg: "bun not found" + cmds: + - bun install + + install:frontend:deps:pnpm: + dir: frontend + sources: + - package.json + - pnpm-lock.yaml + generates: + - node_modules + preconditions: + - sh: pnpm --version + msg: "pnpm not found" + cmds: + - pnpm install + + install:frontend:deps:yarn: + dir: frontend + sources: + - package.json + - yarn.lock + status: + - test -d node_modules || test -f .pnp.cjs + preconditions: + - sh: yarn --version + msg: "yarn not found" + cmds: + - yarn install + + build:frontend: + label: build:frontend (DEV={{.DEV}} RUNNER={{.PACKAGE_MANAGER}}) + summary: Build the frontend project + # darwin:build:universal runs its per-arch builds as parallel deps, each of + # which depends on this task. Without run:once the two executions race: + # one regenerates frontend/bindings (-clean deletes it first) while the + # other's bundler is reading it, failing intermittently with + # 'Could not resolve "./bindings/"' (#4637). + run: once + dir: frontend + sources: + - "**/*" + - exclude: node_modules/**/* + generates: + - dist/**/* + deps: + - task: install:frontend:deps + - task: generate:bindings + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + OBFUSCATED: + ref: .OBFUSCATED + cmds: + - task: frontend:run + vars: + SCRIPT: '{{if eq .DEV "true"}}build:dev{{else}}build{{end}}' + env: + PRODUCTION: '{{if eq .DEV "true"}}false{{else}}true{{end}}' + + frontend:run: + summary: Run a frontend script with selected runner + cmds: + - task: frontend:run:{{.PACKAGE_MANAGER}} + vars: + SCRIPT: "{{.SCRIPT}}" + vars: + SCRIPT: "{{.SCRIPT}}" + + frontend:run:npm: + dir: frontend + cmds: + - npm run {{.SCRIPT}} -q + vars: + SCRIPT: "{{.SCRIPT}}" + + frontend:run:yarn: + dir: frontend + cmds: + - yarn {{.SCRIPT}} + vars: + SCRIPT: "{{.SCRIPT}}" + + frontend:run:pnpm: + dir: frontend + cmds: + - pnpm run {{.SCRIPT}} + vars: + SCRIPT: "{{.SCRIPT}}" + + frontend:run:bun: + dir: frontend + cmds: + - bun run {{.SCRIPT}} + vars: + SCRIPT: "{{.SCRIPT}}" + + frontend:vendor:puppertino: + summary: Fetches Puppertino CSS into frontend/public for consistent mobile styling + sources: + - frontend/public/puppertino/puppertino.css + generates: + - frontend/public/puppertino/puppertino.css + cmds: + - | + set -euo pipefail + mkdir -p frontend/public/puppertino + # If bundled Puppertino exists, prefer it. Otherwise, try to fetch, but don't fail build on error. + if [ ! -f frontend/public/puppertino/puppertino.css ]; then + echo "No bundled Puppertino found. Attempting to fetch from GitHub..." + if curl -fsSL https://raw.githubusercontent.com/codedgar/Puppertino/main/dist/css/full.css -o frontend/public/puppertino/puppertino.css; then + curl -fsSL https://raw.githubusercontent.com/codedgar/Puppertino/main/LICENSE -o frontend/public/puppertino/LICENSE || true + echo "Puppertino CSS downloaded to frontend/public/puppertino/puppertino.css" + else + echo "Warning: Could not fetch Puppertino CSS. Proceeding without download since template may bundle it." + fi + else + echo "Using bundled Puppertino at frontend/public/puppertino/puppertino.css" + fi + # Ensure index.html includes Puppertino CSS and button classes + INDEX_HTML=frontend/index.html + if [ -f "$INDEX_HTML" ]; then + if ! grep -q 'href="/puppertino/puppertino.css"' "$INDEX_HTML"; then + # Insert Puppertino link tag after style.css link + awk ' + /href="\/style.css"\/?/ && !x { print; print " "; x=1; next }1 + ' "$INDEX_HTML" > "$INDEX_HTML.tmp" && mv "$INDEX_HTML.tmp" "$INDEX_HTML" + fi + # Replace default .btn with Puppertino primary button classes if present + sed -E -i'' 's/class=\"btn\"/class=\"p-btn p-prim-col\"/g' "$INDEX_HTML" || true + fi + + + + generate:bindings: + summary: Generates bindings for the frontend + run: once + deps: + - task: go:mod:tidy + sources: + - "**/*.[jt]s" + - exclude: frontend/**/* + - frontend/bindings/**/* # Rerun when switching between dev/production mode causes changes in output + - "**/*.go" + - go.mod + - go.sum + generates: + - frontend/bindings/**/* + cmds: + - wails3 generate bindings -f '{{.BUILD_FLAGS}}' -clean=true{{if eq .OBFUSCATED "true"}} -obfuscated{{end}} -ts -i + + generate:icons: + summary: Generates Windows `.ico` and Mac `.icns` from an image; on macOS, `-iconcomposerinput appicon.icon -macassetdir darwin` also produces `Assets.car` from a `.icon` file (skipped on other platforms). + run: once + dir: build + sources: + - "appicon.png" + - "appicon.icon" + generates: + - "darwin/icons.icns" + - "windows/icon.ico" + cmds: + - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico -iconcomposerinput appicon.icon -macassetdir darwin + + dev:frontend: + summary: Runs the frontend in development mode + deps: + - task: install:frontend:deps + cmds: + - task: frontend:dev:{{.PACKAGE_MANAGER}} + + frontend:dev:npm: + dir: frontend + cmds: + - npm run dev -- --port {{.VITE_PORT}} --strictPort + + frontend:dev:yarn: + dir: frontend + cmds: + - yarn dev --port {{.VITE_PORT}} --strictPort + + frontend:dev:pnpm: + dir: frontend + cmds: + - pnpm dev --port {{.VITE_PORT}} --strictPort + + frontend:dev:bun: + dir: frontend + cmds: + - bun run dev --port {{.VITE_PORT}} --strictPort + + update:build-assets: + summary: Updates the build assets + dir: build + cmds: + - wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir . + + build:server: + summary: Builds the application in server mode (no GUI, HTTP server only) + desc: | + Builds a production server binary by default: -tags server,production, + -trimpath and a stripped binary, mirroring the desktop `build` task. + Server mode runs as a pure HTTP server without native GUI dependencies. + + Usage: task build:server [DEV=true] [OBFUSCATED=true] [EXTRA_TAGS=tag1,tag2] + DEV=true development server (-tags server, no strip, inlining kept) + OBFUSCATED=true obfuscated build via garble (requires garble installed) + EXTRA_TAGS additional comma-separated build tags + deps: + - task: build:frontend + vars: + DEV: + ref: .DEV + BUILD_FLAGS: + ref: .BUILD_FLAGS + OBFUSCATED: + ref: .OBFUSCATED + preconditions: + - sh: '{{if eq .OBFUSCATED "true"}}command -v garble >/dev/null 2>&1{{else}}true{{end}}' + msg: "garble is required for obfuscated builds. Install it with: go install mvdan.cc/garble@v0.16.0 (requires Go 1.24+). See https://github.com/burrowers/garble/releases for version/toolchain compatibility." + cmds: + - '{{if eq .OBFUSCATED "true"}}garble {{.GARBLE_ARGS}} build{{else}}go build{{end}} {{.BUILD_FLAGS}} -o "{{.BIN_DIR}}/{{.APP_NAME}}-server{{exeExt}}"' + vars: + BUILD_FLAGS: '-tags server{{if eq .DEV "true"}}{{if eq .OBFUSCATED "true"}},wails_obfuscated{{end}}{{if .EXTRA_TAGS}},{{.EXTRA_TAGS}}{{end}} -buildvcs=false -gcflags=all="-l"{{else}},production{{if eq .OBFUSCATED "true"}},wails_obfuscated{{end}}{{if .EXTRA_TAGS}},{{.EXTRA_TAGS}}{{end}} -trimpath -buildvcs=false -ldflags="-w -s"{{end}}' + + run:server: + summary: Builds and runs a development server (DEV=true) + deps: + - task: build:server + vars: + DEV: "true" + cmds: + - '"./{{.BIN_DIR}}/{{.APP_NAME}}-server{{exeExt}}"' + + build:docker: + summary: Builds a Docker image for server mode deployment + desc: | + Creates a minimal Docker image containing the production server binary. + Defaults to a pure-Go static binary on a distroless/static base. The + production frontend is built first so the embedded assets are current. + + Usage: task build:docker [TAG=myapp:latest] [CGO_ENABLED=1] [GO_IMAGE=...] [RUNTIME_IMAGE=...] + For CGO apps, set CGO_ENABLED=1 with libc-compatible builder/runtime images, e.g.: + task build:docker CGO_ENABLED=1 GO_IMAGE=golang:bookworm RUNTIME_IMAGE=gcr.io/distroless/base-debian12 + deps: + # Build the production frontend so frontend/dist (embedded by the Go build + # inside the image) is present and current in the Docker build context. + # Pass the server,production tags so binding generation analyses the same + # build the Docker image compiles, not the default-tag build. + - task: build:frontend + vars: + BUILD_FLAGS: "-tags server,production" + cmds: + - >- + docker build + --build-arg CGO_ENABLED={{.CGO_ENABLED | default "0"}} + --build-arg GO_IMAGE={{.GO_IMAGE | default "golang:alpine"}} + --build-arg RUNTIME_IMAGE={{.RUNTIME_IMAGE | default "gcr.io/distroless/static-debian12"}} + -t {{.TAG | default (printf "%s:latest" .APP_NAME)}} + -f build/docker/Dockerfile.server . + vars: + TAG: "{{.TAG}}" + preconditions: + - sh: docker info > /dev/null 2>&1 + msg: "Docker is required. Please install Docker first." + - sh: test -f build/docker/Dockerfile.server + msg: "Dockerfile.server not found. Run 'wails3 update build-assets' to generate it." + + run:docker: + summary: Builds and runs the Docker image + desc: | + Builds the Docker image and runs it, exposing port 8080. + Usage: task run:docker [TAG=myapp:latest] [PORT=8080] + Note: The internal container port is always 8080. The PORT variable + only changes the host port mapping. Ensure your app uses port 8080 + or modify the Dockerfile to match your ServerOptions.Port setting. + deps: + - task: build:docker + vars: + TAG: + ref: .TAG + cmds: + - docker run --rm -p {{.PORT | default "8080"}}:8080 {{.TAG | default (printf "%s:latest" .APP_NAME)}} + vars: + TAG: "{{.TAG}}" + PORT: "{{.PORT}}" + + setup:docker: + summary: Builds Docker image for cross-compilation (~800MB download) + desc: | + Builds the Docker image needed for cross-compiling to any platform. + Run this once to enable cross-platform builds from any OS. + cmds: + - docker build -t wails-cross -f build/docker/Dockerfile.cross build/docker/ + preconditions: + - sh: docker info > /dev/null 2>&1 + msg: "Docker is required. Please install Docker first." + + ios:device:list: + summary: Lists connected iOS devices (UDIDs) + cmds: + - xcrun xcdevice list + + ios:run:device: + summary: Build, install, and launch on a physical iPhone using Apple tools (xcodebuild/devicectl) + vars: + PROJECT: '{{.PROJECT}}' # e.g., build/ios/xcode/.xcodeproj + SCHEME: '{{.SCHEME}}' # e.g., ios.dev + CONFIG: '{{.CONFIG | default "Debug"}}' + DERIVED: '{{.DERIVED | default "build/ios/DerivedData"}}' + UDID: '{{.UDID}}' # from `task ios:device:list` + BUNDLE_ID: '{{.BUNDLE_ID}}' # e.g., com.yourco.wails.ios.dev + TEAM_ID: '{{.TEAM_ID}}' # optional, if your project is not already set up for signing + preconditions: + - sh: xcrun -f xcodebuild + msg: "xcodebuild not found. Please install Xcode." + - sh: xcrun -f devicectl + msg: "devicectl not found. Please update to Xcode 15+ (which includes devicectl)." + - sh: test -n '{{.PROJECT}}' + msg: "Set PROJECT to your .xcodeproj path (e.g., PROJECT=build/ios/xcode/App.xcodeproj)." + - sh: test -n '{{.SCHEME}}' + msg: "Set SCHEME to your app scheme (e.g., SCHEME=ios.dev)." + - sh: test -n '{{.UDID}}' + msg: "Set UDID to your device UDID (see: task ios:device:list)." + - sh: test -n '{{.BUNDLE_ID}}' + msg: "Set BUNDLE_ID to your app's bundle identifier (e.g., com.yourco.wails.ios.dev)." + cmds: + - | + set -euo pipefail + echo "Building for device: UDID={{.UDID}} SCHEME={{.SCHEME}} PROJECT={{.PROJECT}}" + XCB_ARGS=( + -project "{{.PROJECT}}" + -scheme "{{.SCHEME}}" + -configuration "{{.CONFIG}}" + -destination "id={{.UDID}}" + -derivedDataPath "{{.DERIVED}}" + -allowProvisioningUpdates + -allowProvisioningDeviceRegistration + ) + # Optionally inject signing identifiers if provided + if [ -n '{{.TEAM_ID}}' ]; then XCB_ARGS+=(DEVELOPMENT_TEAM={{.TEAM_ID}}); fi + if [ -n '{{.BUNDLE_ID}}' ]; then XCB_ARGS+=(PRODUCT_BUNDLE_IDENTIFIER={{.BUNDLE_ID}}); fi + xcodebuild "${XCB_ARGS[@]}" build | xcpretty || true + # If xcpretty isn't installed, run without it + if [ "${PIPESTATUS[0]}" -ne 0 ]; then + xcodebuild "${XCB_ARGS[@]}" build + fi + # Find built .app + APP_PATH=$(find "{{.DERIVED}}/Build/Products" -type d -name "*.app" -maxdepth 3 | head -n 1) + if [ -z "$APP_PATH" ]; then + echo "Could not locate built .app under {{.DERIVED}}/Build/Products" >&2 + exit 1 + fi + echo "Installing: $APP_PATH" + xcrun devicectl device install app --device "{{.UDID}}" "$APP_PATH" + echo "Launching: {{.BUNDLE_ID}}" + xcrun devicectl device process launch --device "{{.UDID}}" --stderr console --stdout console "{{.BUNDLE_ID}}" diff --git a/sundynix-desktop/build/appicon.icon/Assets/wails_icon_vector.svg b/sundynix-desktop/build/appicon.icon/Assets/wails_icon_vector.svg new file mode 100644 index 0000000..b099222 --- /dev/null +++ b/sundynix-desktop/build/appicon.icon/Assets/wails_icon_vector.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sundynix-desktop/build/appicon.icon/icon.json b/sundynix-desktop/build/appicon.icon/icon.json new file mode 100644 index 0000000..001a70c --- /dev/null +++ b/sundynix-desktop/build/appicon.icon/icon.json @@ -0,0 +1,51 @@ +{ + "fill" : { + "automatic-gradient" : "extended-gray:1.00000,1.00000" + }, + "groups" : [ + { + "layers" : [ + { + "fill-specializations" : [ + { + "appearance" : "dark", + "value" : { + "solid" : "srgb:0.92143,0.92145,0.92144,1.00000" + } + }, + { + "appearance" : "tinted", + "value" : { + "solid" : "srgb:0.83742,0.83744,0.83743,1.00000" + } + } + ], + "image-name" : "wails_icon_vector.svg", + "name" : "wails_icon_vector", + "position" : { + "scale" : 1.5, + "translation-in-points" : [ + 0.0, + 0.0 + ] + } + } + ], + "shadow" : { + "kind" : "neutral", + "opacity" : 0.5 + }, + "specular" : true, + "translucency" : { + "enabled" : true, + "value" : 0.5 + } + } + ], + "supported-platforms" : { + "circles" : [ + "watchOS" + ], + "squares" : "shared" + } +} \ No newline at end of file diff --git a/sundynix-desktop/build/appicon.png b/sundynix-desktop/build/appicon.png new file mode 100644 index 0000000..63617fe Binary files /dev/null and b/sundynix-desktop/build/appicon.png differ diff --git a/sundynix-desktop/build/config.yml b/sundynix-desktop/build/config.yml new file mode 100644 index 0000000..761dd00 --- /dev/null +++ b/sundynix-desktop/build/config.yml @@ -0,0 +1,79 @@ +# This file contains the configuration for this project. +# When you update `info` or `fileAssociations`, run `wails3 task common:update:build-assets` to update the assets. +# Note that this will overwrite any changes you have made to the assets. +version: '3' + +# This information is used to generate the build assets. +info: + companyName: "sundynix" + productName: "sundynix-agentix" + productIdentifier: "com.sundynix.agentix" + description: "事件驱动的 AI Agent 工作台" + copyright: "(c) 2026, sundynix" + comments: "sundynix-agentix desktop" + version: "0.1.0" + # cfBundleIconName: "appicon" # The macOS icon name in Assets.car icon bundles (optional) + # # Should match the name of your .icon file without the extension + # # If not set and Assets.car exists, defaults to "appicon" + +# iOS build configuration (uncomment to customise iOS project generation) +# Note: Keys under `ios` OVERRIDE values under `info` when set. +# ios: +# # The iOS bundle identifier used in the generated Xcode project (CFBundleIdentifier) +# bundleID: "com.mycompany.myproduct" +# # The display name shown under the app icon (CFBundleDisplayName/CFBundleName) +# displayName: "My Product" +# # The app version to embed in Info.plist (CFBundleShortVersionString/CFBundleVersion) +# version: "0.0.1" +# # The company/organisation name for templates and project settings +# company: "My Company" +# # Additional comments to embed in Info.plist metadata +# comments: "Some Product Comments" + +# Dev mode configuration +dev_mode: + root_path: . + log_level: warn + debounce: 1000 + ignore: + dir: + - .git + - node_modules + - frontend + - bin + file: + - .DS_Store + - .gitignore + - .gitkeep + - "*_test.go" + watched_extension: + - "*.go" + - "*.js" # Watch for changes to JS/TS files included using the //wails:include directive. + - "*.ts" # The frontend directory will be excluded entirely by the setting above. + git_ignore: true + executes: + - cmd: wails3 build DEV=true + type: blocking + - cmd: wails3 task common:dev:frontend + type: background + - cmd: wails3 task run + type: primary + +# File Associations +# More information at: https://v3.wails.io/noit/done/yet +fileAssociations: +# - ext: wails +# name: Wails +# description: Wails Application File +# iconName: wailsFileIcon +# role: Editor +# - ext: jpg +# name: JPEG +# description: Image File +# iconName: jpegFileIcon +# role: Editor +# mimeType: image/jpeg # (optional) + +# Other data +other: + - name: My Other Data \ No newline at end of file diff --git a/sundynix-desktop/build/darwin/Assets.car b/sundynix-desktop/build/darwin/Assets.car new file mode 100644 index 0000000..32e5ad9 Binary files /dev/null and b/sundynix-desktop/build/darwin/Assets.car differ diff --git a/sundynix-desktop/build/darwin/Info.dev.plist b/sundynix-desktop/build/darwin/Info.dev.plist new file mode 100644 index 0000000..292b836 --- /dev/null +++ b/sundynix-desktop/build/darwin/Info.dev.plist @@ -0,0 +1,34 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + wails3ref + CFBundleIdentifier + com.example.wails3ref + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + CFBundleIconName + appicon + LSMinimumSystemVersion + 12.0.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © 2026, My Company + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + \ No newline at end of file diff --git a/sundynix-desktop/build/darwin/Info.plist b/sundynix-desktop/build/darwin/Info.plist new file mode 100644 index 0000000..f4b1bd8 --- /dev/null +++ b/sundynix-desktop/build/darwin/Info.plist @@ -0,0 +1,29 @@ + + + + CFBundlePackageType + APPL + CFBundleName + My Product + CFBundleExecutable + wails3ref + CFBundleIdentifier + com.example.wails3ref + CFBundleVersion + 0.1.0 + CFBundleGetInfoString + This is a comment + CFBundleShortVersionString + 0.1.0 + CFBundleIconFile + icons + CFBundleIconName + appicon + LSMinimumSystemVersion + 12.0.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + © 2026, My Company + + \ No newline at end of file diff --git a/sundynix-desktop/build/darwin/Taskfile.yml b/sundynix-desktop/build/darwin/Taskfile.yml new file mode 100644 index 0000000..1217537 --- /dev/null +++ b/sundynix-desktop/build/darwin/Taskfile.yml @@ -0,0 +1,193 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +vars: + # Docker image for cross-compilation (used when building on non-macOS) + CROSS_IMAGE: wails-cross + +tasks: + build: + summary: Builds the application + cmds: + - task: '{{if eq OS "darwin"}}build:native{{else}}build:docker{{end}}' + vars: + ARCH: '{{.ARCH}}' + DEV: '{{.DEV}}' + OUTPUT: '{{.OUTPUT}}' + EXTRA_TAGS: '{{.EXTRA_TAGS}}' + OBFUSCATED: '{{.OBFUSCATED}}' + GARBLE_ARGS: '{{.GARBLE_ARGS}}' + vars: + DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}' + OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}' + + build:native: + summary: Builds the application natively on macOS + internal: true + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + OBFUSCATED: + ref: .OBFUSCATED + DEV: + ref: .DEV + - task: common:generate:icons + preconditions: + - sh: '{{if eq .OBFUSCATED "true"}}command -v garble >/dev/null 2>&1{{else}}true{{end}}' + msg: "garble is required for obfuscated builds. Install it with: go install mvdan.cc/garble@v0.16.0 (requires Go 1.24+). See https://github.com/burrowers/garble/releases for version/toolchain compatibility." + cmds: + - '{{if eq .OBFUSCATED "true"}}garble {{.GARBLE_ARGS}} build{{else}}go build{{end}} {{.BUILD_FLAGS}} -o "{{.OUTPUT}}"' + vars: + BUILD_FLAGS: '{{if eq .DEV "true"}}{{if or .EXTRA_TAGS (eq .OBFUSCATED "true")}}-tags {{if eq .OBFUSCATED "true"}}wails_obfuscated{{if .EXTRA_TAGS}},{{end}}{{end}}{{.EXTRA_TAGS}} {{end}}-buildvcs=false -gcflags=all="-l"{{else}}-tags production{{if eq .OBFUSCATED "true"}},wails_obfuscated{{end}}{{if .EXTRA_TAGS}},{{.EXTRA_TAGS}}{{end}} -trimpath -buildvcs=false -ldflags="-w -s"{{end}}' + DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}' + OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}' + env: + GOOS: darwin + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + CGO_CFLAGS: "-mmacosx-version-min=12.0" + CGO_LDFLAGS: "-mmacosx-version-min=12.0" + MACOSX_DEPLOYMENT_TARGET: "12.0" + + build:docker: + summary: Cross-compiles for macOS using Docker (for Linux/Windows hosts) + internal: true + deps: + - task: common:build:frontend + vars: + OBFUSCATED: + ref: .OBFUSCATED + - task: common:generate:icons + preconditions: + - sh: docker info > /dev/null 2>&1 + msg: "Docker is required for cross-compilation. Please install Docker." + - sh: docker image inspect {{.CROSS_IMAGE}} > /dev/null 2>&1 + msg: | + Docker image '{{.CROSS_IMAGE}}' not found. + Build it first: wails3 task setup:docker + cmds: + - docker run --rm -v "{{.ROOT_DIR}}:/app" {{.DOCKER_MOUNTS}} -e APP_NAME="{{.APP_NAME}}" {{if .EXTRA_TAGS}}-e EXTRA_TAGS="{{.EXTRA_TAGS}}"{{end}} {{if eq .OBFUSCATED "true"}}-e OBFUSCATED=true{{end}} {{if .GARBLE_ARGS}}-e GARBLE_ARGS="{{.GARBLE_ARGS}}"{{end}} {{.CROSS_IMAGE}} darwin {{.DOCKER_ARCH}} + - cmd: docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin + platforms: [linux, darwin] + - mkdir -p {{.BIN_DIR}} + - mv "bin/{{.APP_NAME}}-darwin-{{.DOCKER_ARCH}}" "{{.OUTPUT}}" + vars: + DOCKER_ARCH: '{{if eq .ARCH "arm64"}}arm64{{else if eq .ARCH "amd64"}}amd64{{else}}arm64{{end}}' + DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}' + OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}' + # Generate Docker volume mounts: Go module cache + go.mod replace directives + # Uses wails3 tool docker-mounts for cross-platform compatibility (Windows/Linux/macOS) + DOCKER_MOUNTS: + sh: 'wails3 tool docker-mounts' + + build:universal: + summary: Builds darwin universal binary (arm64 + amd64) + deps: + - task: build + vars: + ARCH: amd64 + OUTPUT: "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" + - task: build + vars: + ARCH: arm64 + OUTPUT: "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + cmds: + - task: '{{if eq OS "darwin"}}build:universal:lipo:native{{else}}build:universal:lipo:go{{end}}' + + build:universal:lipo:native: + summary: Creates universal binary using native lipo (macOS) + internal: true + cmds: + - lipo -create -output "{{.BIN_DIR}}/{{.APP_NAME}}" "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + - rm "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + + build:universal:lipo:go: + summary: Creates universal binary using wails3 tool lipo (Linux/Windows) + internal: true + cmds: + - wails3 tool lipo -output "{{.BIN_DIR}}/{{.APP_NAME}}" -input "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" -input "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + - rm -f "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + + package: + summary: Packages the application into a `.app` bundle + deps: + - task: build + cmds: + - task: create:app:bundle + + package:universal: + summary: Packages darwin universal binary (arm64 + amd64) + deps: + - task: build:universal + cmds: + - task: create:app:bundle + + + create:app:bundle: + summary: Creates an `.app` bundle + cmds: + - mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS" + - mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources" + - cp build/darwin/icons.icns "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources" + - | + if [ -f build/darwin/Assets.car ]; then + cp build/darwin/Assets.car "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources" + fi + - cp "{{.BIN_DIR}}/{{.APP_NAME}}" "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS" + - cp build/darwin/Info.plist "{{.BIN_DIR}}/{{.APP_NAME}}.app/Contents" + - task: '{{if eq OS "darwin"}}codesign:adhoc{{else}}codesign:skip{{end}}' + + codesign:adhoc: + summary: Ad-hoc signs the app bundle (macOS only) + internal: true + cmds: + - codesign --force --deep --sign - "{{.BIN_DIR}}/{{.APP_NAME}}.app" + + codesign:skip: + summary: Skips codesigning when cross-compiling + internal: true + cmds: + - 'echo "Skipping codesign (not available on {{OS}}). Sign the .app on macOS before distribution."' + + run: + cmds: + - mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS" + - mkdir -p "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources" + - cp build/darwin/icons.icns "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources" + - | + if [ -f build/darwin/Assets.car ]; then + cp build/darwin/Assets.car "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Resources" + fi + - cp "{{.BIN_DIR}}/{{.APP_NAME}}" "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS" + - cp "build/darwin/Info.dev.plist" "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/Info.plist" + - codesign --force --deep --sign - "{{.BIN_DIR}}/{{.APP_NAME}}.dev.app" + - '"{{.BIN_DIR}}/{{.APP_NAME}}.dev.app/Contents/MacOS/{{.APP_NAME}}"' + + sign: + summary: Signs the application bundle with Developer ID + desc: | + Signs the .app bundle for distribution. + Uses signing identity from `wails3 setup` (stored in ~/.config/wails/defaults.yaml). + Override with: task darwin:sign -- --identity "Developer ID Application: ..." + deps: + - task: package + cmds: + - wails3 tool sign --input "{{.BIN_DIR}}/{{.APP_NAME}}.app" {{.CLI_ARGS}} + + sign:notarize: + summary: Signs and notarizes the application bundle + desc: | + Signs the .app bundle and submits it for notarization. + Uses signing identity and keychain profile from `wails3 setup`. + + First-time setup: + wails3 setup + deps: + - task: package + cmds: + - wails3 tool sign --input "{{.BIN_DIR}}/{{.APP_NAME}}.app" --notarize {{.CLI_ARGS}} diff --git a/sundynix-desktop/build/darwin/icons.icns b/sundynix-desktop/build/darwin/icons.icns new file mode 100644 index 0000000..93c7299 Binary files /dev/null and b/sundynix-desktop/build/darwin/icons.icns differ diff --git a/sundynix-desktop/build/docker/Dockerfile.cross b/sundynix-desktop/build/docker/Dockerfile.cross new file mode 100644 index 0000000..a9be99e --- /dev/null +++ b/sundynix-desktop/build/docker/Dockerfile.cross @@ -0,0 +1,220 @@ +# Cross-compile Wails v3 apps to any platform +# +# Darwin: Zig + macOS SDK +# Linux: Native GCC when host matches target, Zig for cross-arch +# Windows: Zig + bundled mingw +# +# Usage: +# docker build -t wails-cross -f Dockerfile.cross . +# docker run --rm -v $(pwd):/app wails-cross darwin arm64 +# docker run --rm -v $(pwd):/app wails-cross darwin amd64 +# docker run --rm -v $(pwd):/app wails-cross linux amd64 +# docker run --rm -v $(pwd):/app wails-cross linux arm64 +# docker run --rm -v $(pwd):/app wails-cross windows amd64 +# docker run --rm -v $(pwd):/app wails-cross windows arm64 + +FROM golang:1.26-bookworm + +ARG TARGETARCH +ARG GARBLE_VERSION=v0.16.0 + +# Install base tools, GCC, and GTK/WebKit dev packages +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl xz-utils python3 nodejs npm pkg-config gcc libc6-dev \ + libgtk-3-dev libwebkit2gtk-4.1-dev \ + libgtk-4-dev libwebkitgtk-6.0-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN go install mvdan.cc/garble@${GARBLE_VERSION} + +# Install Zig - automatically selects correct binary for host architecture. +# The SHA-256 is fetched from Zig's official download manifest and verified +# before extraction to protect against MITM or corrupted downloads. +ARG ZIG_VERSION=0.14.0 +RUN ZIG_ARCH=$(case "${TARGETARCH}" in arm64) echo "aarch64" ;; *) echo "x86_64" ;; esac) && \ + ZIG_FILE="zig-linux-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz" && \ + curl -fSL "https://ziglang.org/download/${ZIG_VERSION}/${ZIG_FILE}" -o "/tmp/${ZIG_FILE}" && \ + EXPECTED=$(curl -fsSL "https://ziglang.org/download/index.json" | \ + python3 -c "import json,sys; d=json.load(sys.stdin); print(d[sys.argv[1]][sys.argv[2]+'-linux']['shasum'])" \ + "${ZIG_VERSION}" "${ZIG_ARCH}") && \ + echo "${EXPECTED} /tmp/${ZIG_FILE}" | sha256sum -c - && \ + tar -xJ -f "/tmp/${ZIG_FILE}" -C /opt && \ + rm "/tmp/${ZIG_FILE}" && \ + ln -s /opt/zig-linux-${ZIG_ARCH}-${ZIG_VERSION}/zig /usr/local/bin/zig + +# Download macOS SDK (required for darwin targets) +ARG MACOS_SDK_VERSION=14.5 +RUN curl -L "https://github.com/joseluisq/macosx-sdks/releases/download/${MACOS_SDK_VERSION}/MacOSX${MACOS_SDK_VERSION}.sdk.tar.xz" \ + | tar -xJ -C /opt \ + && mv /opt/MacOSX${MACOS_SDK_VERSION}.sdk /opt/macos-sdk + +ENV MACOS_SDK_PATH=/opt/macos-sdk + +# Create Zig CC wrappers for cross-compilation targets +# Darwin and Windows use Zig; Linux uses native GCC (run with --platform for cross-arch) + +# Darwin arm64 +COPY <<'ZIGWRAP' /usr/local/bin/zcc-darwin-arm64 +#!/bin/sh +ARGS="" +SKIP_NEXT=0 +for arg in "$@"; do + if [ $SKIP_NEXT -eq 1 ]; then + SKIP_NEXT=0 + continue + fi + case "$arg" in + -target) SKIP_NEXT=1 ;; + -mmacosx-version-min=*) ;; + *) ARGS="$ARGS $arg" ;; + esac +done +exec zig cc -fno-sanitize=all -target aarch64-macos-none -isysroot /opt/macos-sdk -I/opt/macos-sdk/usr/include -L/opt/macos-sdk/usr/lib -F/opt/macos-sdk/System/Library/Frameworks -w $ARGS +ZIGWRAP +RUN chmod +x /usr/local/bin/zcc-darwin-arm64 + +# Darwin amd64 +COPY <<'ZIGWRAP' /usr/local/bin/zcc-darwin-amd64 +#!/bin/sh +ARGS="" +SKIP_NEXT=0 +for arg in "$@"; do + if [ $SKIP_NEXT -eq 1 ]; then + SKIP_NEXT=0 + continue + fi + case "$arg" in + -target) SKIP_NEXT=1 ;; + -mmacosx-version-min=*) ;; + *) ARGS="$ARGS $arg" ;; + esac +done +exec zig cc -fno-sanitize=all -target x86_64-macos-none -isysroot /opt/macos-sdk -I/opt/macos-sdk/usr/include -L/opt/macos-sdk/usr/lib -F/opt/macos-sdk/System/Library/Frameworks -w $ARGS +ZIGWRAP +RUN chmod +x /usr/local/bin/zcc-darwin-amd64 + +# Windows amd64 - uses Zig's bundled mingw +COPY <<'ZIGWRAP' /usr/local/bin/zcc-windows-amd64 +#!/bin/sh +ARGS="" +SKIP_NEXT=0 +for arg in "$@"; do + if [ $SKIP_NEXT -eq 1 ]; then + SKIP_NEXT=0 + continue + fi + case "$arg" in + -target) SKIP_NEXT=1 ;; + -Wl,*) ;; + *) ARGS="$ARGS $arg" ;; + esac +done +exec zig cc -target x86_64-windows-gnu $ARGS +ZIGWRAP +RUN chmod +x /usr/local/bin/zcc-windows-amd64 + +# Windows arm64 - uses Zig's bundled mingw +COPY <<'ZIGWRAP' /usr/local/bin/zcc-windows-arm64 +#!/bin/sh +ARGS="" +SKIP_NEXT=0 +for arg in "$@"; do + if [ $SKIP_NEXT -eq 1 ]; then + SKIP_NEXT=0 + continue + fi + case "$arg" in + -target) SKIP_NEXT=1 ;; + -Wl,*) ;; + *) ARGS="$ARGS $arg" ;; + esac +done +exec zig cc -target aarch64-windows-gnu $ARGS +ZIGWRAP +RUN chmod +x /usr/local/bin/zcc-windows-arm64 + +# Build script +COPY <<'SCRIPT' /usr/local/bin/build.sh +#!/bin/sh +set -e + +OS=${1:-darwin} +ARCH=${2:-arm64} + +case "${OS}-${ARCH}" in + darwin-arm64|darwin-aarch64) + export CC=zcc-darwin-arm64 + export GOARCH=arm64 + export GOOS=darwin + ;; + darwin-amd64|darwin-x86_64) + export CC=zcc-darwin-amd64 + export GOARCH=amd64 + export GOOS=darwin + ;; + linux-arm64|linux-aarch64) + export CC=gcc + export GOARCH=arm64 + export GOOS=linux + ;; + linux-amd64|linux-x86_64) + export CC=gcc + export GOARCH=amd64 + export GOOS=linux + ;; + windows-arm64|windows-aarch64) + export CC=zcc-windows-arm64 + export GOARCH=arm64 + export GOOS=windows + ;; + windows-amd64|windows-x86_64) + export CC=zcc-windows-amd64 + export GOARCH=amd64 + export GOOS=windows + ;; + *) + echo "Usage: " + echo " os: darwin, linux, windows" + echo " arch: amd64, arm64" + exit 1 + ;; +esac + +export CGO_ENABLED=1 +export CGO_CFLAGS="-w" + +# Build frontend if exists and not already built (host may have built it) +if [ -d "frontend" ] && [ -f "frontend/package.json" ] && [ ! -d "frontend/dist" ]; then + (cd frontend && npm install --silent && npm run build --silent) +fi + +# Build +APP=${APP_NAME:-$(basename $(pwd))} +mkdir -p bin + +EXT="" +LDFLAGS="-s -w" +if [ "$GOOS" = "windows" ]; then + EXT=".exe" + LDFLAGS="-s -w -H windowsgui" +fi + +TAGS="production" +if [ -n "$EXTRA_TAGS" ]; then + TAGS="${TAGS},${EXTRA_TAGS}" +fi + +COMPILER="go build" +if [ "$OBFUSCATED" = "true" ]; then + COMPILER="garble ${GARBLE_ARGS} build" + TAGS="${TAGS},wails_obfuscated" +fi + +${COMPILER} -tags "$TAGS" -trimpath -buildvcs=false -ldflags="$LDFLAGS" -o bin/${APP}-${GOOS}-${GOARCH}${EXT} . +echo "Built: bin/${APP}-${GOOS}-${GOARCH}${EXT}" +SCRIPT +RUN chmod +x /usr/local/bin/build.sh + +WORKDIR /app +ENTRYPOINT ["/usr/local/bin/build.sh"] +CMD ["darwin", "arm64"] diff --git a/sundynix-desktop/build/docker/Dockerfile.server b/sundynix-desktop/build/docker/Dockerfile.server new file mode 100644 index 0000000..7afbae3 --- /dev/null +++ b/sundynix-desktop/build/docker/Dockerfile.server @@ -0,0 +1,67 @@ +# Wails Server Mode Dockerfile +# Multi-stage build for a minimal image. +# +# Defaults produce a pure-Go, fully static binary on a distroless/static base: +# the smallest, most secure image, and correct for the default (CGO-free) app. +# +# If your app needs CGO, override the build args, e.g.: +# docker build --build-arg CGO_ENABLED=1 \ +# --build-arg GO_IMAGE=golang:bookworm \ +# --build-arg RUNTIME_IMAGE=gcr.io/distroless/base-debian12 \ +# -f build/docker/Dockerfile.server . +# CGO needs a C toolchain in the builder and a libc in the runtime image; keep +# the builder and runtime libc compatible (a glibc builder -> a glibc runtime). +# The Taskfile passes these through: task build:docker CGO_ENABLED=1 GO_IMAGE=... RUNTIME_IMAGE=... + +ARG GO_IMAGE=golang:alpine +ARG RUNTIME_IMAGE=gcr.io/distroless/static-debian12 +ARG CGO_ENABLED=0 + +# Build stage +FROM ${GO_IMAGE} AS builder +ARG CGO_ENABLED + +WORKDIR /app + +# Install build dependencies: git always; a C compiler only when CGO is enabled. +# Supports both apk (alpine) and apt (debian/bookworm) base images. +RUN if command -v apk >/dev/null 2>&1; then \ + apk add --no-cache git $([ "$CGO_ENABLED" = "1" ] && echo gcc musl-dev); \ + else \ + apt-get update && apt-get install -y --no-install-recommends git $([ "$CGO_ENABLED" = "1" ] && echo gcc libc6-dev) && rm -rf /var/lib/apt/lists/*; \ + fi + +# Copy source code +COPY . . + +# Remove local replace directive if present (for production builds) +RUN sed -i '/^replace/d' go.mod || true + +# Download dependencies +RUN go mod tidy + +# Build the production server binary. +# - server,production tags: HTTP server mode with production code paths (no dev +# logger/asset middleware), matching the desktop production build. +# - CGO_ENABLED defaults to 0 for a fully static binary (distroless/static); +# override with --build-arg CGO_ENABLED=1 (see header) for CGO apps. +RUN CGO_ENABLED=${CGO_ENABLED} go build -tags server,production -trimpath -buildvcs=false -ldflags="-s -w" -o server . + +# Runtime stage - minimal image +FROM ${RUNTIME_IMAGE} + +# Copy the binary +COPY --from=builder /app/server /server + +# Copy frontend assets +COPY --from=builder /app/frontend/dist /frontend/dist + +# Expose the default port +EXPOSE 8080 + +# Bind to all interfaces (required for Docker) +# Can be overridden at runtime with -e WAILS_SERVER_HOST=... +ENV WAILS_SERVER_HOST=0.0.0.0 + +# Run the server +ENTRYPOINT ["/server"] diff --git a/sundynix-desktop/build/linux/Taskfile.yml b/sundynix-desktop/build/linux/Taskfile.yml new file mode 100644 index 0000000..496ee95 --- /dev/null +++ b/sundynix-desktop/build/linux/Taskfile.yml @@ -0,0 +1,220 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +vars: + # Signing configuration - edit these values for your project + # PGP_KEY: "path/to/signing-key.asc" + # SIGN_ROLE: "builder" # Options: origin, maint, archive, builder + # + # Password is stored securely in system keychain. Run: wails3 setup signing + + # Docker image for cross-compilation (used when building on non-Linux or no CC available) + CROSS_IMAGE: wails-cross + +tasks: + build: + summary: Builds the application for Linux + cmds: + # Linux requires CGO - use Docker when: + # 1. Cross-compiling from non-Linux, OR + # 2. No C compiler is available, OR + # 3. Target architecture differs from host architecture (cross-arch compilation) + - task: '{{if and (eq OS "linux") (eq .HAS_CC "true") (eq .TARGET_ARCH ARCH)}}build:native{{else}}build:docker{{end}}' + vars: + ARCH: '{{.ARCH}}' + DEV: '{{.DEV}}' + OUTPUT: '{{.OUTPUT}}' + EXTRA_TAGS: '{{.EXTRA_TAGS}}' + OBFUSCATED: '{{.OBFUSCATED}}' + GARBLE_ARGS: '{{.GARBLE_ARGS}}' + vars: + DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}' + OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}' + # Determine target architecture (defaults to host ARCH if not specified) + TARGET_ARCH: '{{.ARCH | default ARCH}}' + # Check if a C compiler is available (gcc or clang) — cross-platform via wails3 tool + HAS_CC: + sh: 'wails3 tool has "gcc|clang"' + + build:native: + summary: Builds the application natively on Linux + internal: true + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + OBFUSCATED: + ref: .OBFUSCATED + DEV: + ref: .DEV + - task: common:generate:icons + - task: generate:dotdesktop + preconditions: + - sh: '{{if eq .OBFUSCATED "true"}}command -v garble >/dev/null 2>&1{{else}}true{{end}}' + msg: "garble is required for obfuscated builds. Install it with: go install mvdan.cc/garble@v0.16.0 (requires Go 1.24+). See https://github.com/burrowers/garble/releases for version/toolchain compatibility." + cmds: + - '{{if eq .OBFUSCATED "true"}}garble {{.GARBLE_ARGS}} build{{else}}go build{{end}} {{.BUILD_FLAGS}} -o {{.OUTPUT}}' + vars: + BUILD_FLAGS: '{{if eq .DEV "true"}}{{if or .EXTRA_TAGS (eq .OBFUSCATED "true")}}-tags {{if eq .OBFUSCATED "true"}}wails_obfuscated{{if .EXTRA_TAGS}},{{end}}{{end}}{{.EXTRA_TAGS}} {{end}}-buildvcs=false -gcflags=all="-l"{{else}}-tags production{{if eq .OBFUSCATED "true"}},wails_obfuscated{{end}}{{if .EXTRA_TAGS}},{{.EXTRA_TAGS}}{{end}} -trimpath -buildvcs=false -ldflags="-w -s"{{end}}' + DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}' + OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}' + env: + GOOS: linux + CGO_ENABLED: 1 + GOARCH: '{{.ARCH | default ARCH}}' + + build:docker: + summary: Builds for Linux using Docker (for non-Linux hosts or when no C compiler available) + internal: true + deps: + - task: common:build:frontend + vars: + OBFUSCATED: + ref: .OBFUSCATED + - task: common:generate:icons + - task: generate:dotdesktop + preconditions: + - sh: docker info > /dev/null 2>&1 + msg: "Docker is required for cross-compilation to Linux. Please install Docker." + - sh: docker image inspect {{.CROSS_IMAGE}} > /dev/null 2>&1 + msg: | + Docker image '{{.CROSS_IMAGE}}' not found. + Build it first: wails3 task setup:docker + cmds: + - docker run --rm -v "{{.ROOT_DIR}}:/app" {{.DOCKER_MOUNTS}} -e APP_NAME="{{.APP_NAME}}" {{if .EXTRA_TAGS}}-e EXTRA_TAGS="{{.EXTRA_TAGS}}"{{end}} {{if eq .OBFUSCATED "true"}}-e OBFUSCATED=true{{end}} {{if .GARBLE_ARGS}}-e GARBLE_ARGS="{{.GARBLE_ARGS}}"{{end}} "{{.CROSS_IMAGE}}" linux {{.DOCKER_ARCH}} + - cmd: docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin + platforms: [linux, darwin] + - mkdir -p {{.BIN_DIR}} + - mv "bin/{{.APP_NAME}}-linux-{{.DOCKER_ARCH}}" "{{.OUTPUT}}" + vars: + DOCKER_ARCH: '{{.ARCH | default "amd64"}}' + DEFAULT_OUTPUT: '{{.BIN_DIR}}/{{.APP_NAME}}' + OUTPUT: '{{ .OUTPUT | default .DEFAULT_OUTPUT }}' + # Generate Docker volume mounts: Go module cache + go.mod replace directives + # Uses wails3 tool docker-mounts for cross-platform compatibility (Windows/Linux/macOS) + DOCKER_MOUNTS: + sh: 'wails3 tool docker-mounts' + + package: + summary: Packages the application for Linux + deps: + - task: build + cmds: + - task: create:appimage + - task: create:deb + - task: create:rpm + - task: create:aur + + create:appimage: + summary: Creates an AppImage + dir: build/linux/appimage + deps: + - task: build + - task: generate:dotdesktop + cmds: + - cp "{{.APP_BINARY}}" "{{.APP_NAME}}" + - cp ../../appicon.png "{{.APP_NAME}}.png" + - wails3 generate appimage -binary "{{.APP_NAME}}" -icon {{.ICON}} -desktopfile {{.DESKTOP_FILE}} -outputdir {{.OUTPUT_DIR}} -builddir {{.ROOT_DIR}}/build/linux/appimage/build + vars: + APP_NAME: '{{.APP_NAME}}' + APP_BINARY: '../../../bin/{{.APP_NAME}}' + ICON: '{{.APP_NAME}}.png' + DESKTOP_FILE: '../{{.APP_NAME}}.desktop' + OUTPUT_DIR: '../../../bin' + + create:deb: + summary: Creates a deb package + deps: + - task: build + cmds: + - task: generate:dotdesktop + - task: generate:deb + + create:rpm: + summary: Creates a rpm package + deps: + - task: build + cmds: + - task: generate:dotdesktop + - task: generate:rpm + + create:aur: + summary: Creates a arch linux packager package + deps: + - task: build + cmds: + - task: generate:dotdesktop + - task: generate:aur + + generate:deb: + summary: Creates a deb package + cmds: + - wails3 tool package -name "{{.APP_NAME}}" -format deb -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin + + generate:rpm: + summary: Creates a rpm package + cmds: + - wails3 tool package -name "{{.APP_NAME}}" -format rpm -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin + + generate:aur: + summary: Creates a arch linux packager package + cmds: + - wails3 tool package -name "{{.APP_NAME}}" -format archlinux -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin + + generate:dotdesktop: + summary: Generates a `.desktop` file + dir: build + cmds: + - mkdir -p {{.ROOT_DIR}}/build/linux/appimage + - wails3 generate .desktop -name "{{.APP_NAME}}" -exec "{{.EXEC}}" -icon "{{.ICON}}" -outputfile "{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop" -categories "{{.CATEGORIES}}" + vars: + APP_NAME: '{{.APP_NAME}}' + EXEC: '{{.APP_NAME}}' + ICON: '{{.APP_NAME}}' + CATEGORIES: 'Development;' + OUTPUTFILE: '{{.ROOT_DIR}}/build/linux/{{.APP_NAME}}.desktop' + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}' + + sign:deb: + summary: Signs the DEB package + desc: | + Signs the .deb package with a PGP key. + Set PGP_KEY in the vars section to override the key configured globally via + `wails3 setup` (~/.config/wails/defaults.yaml). + Password is retrieved from system keychain (run: wails3 setup signing) + deps: + - task: create:deb + cmds: + # PGP_KEY is optional: if unset, `wails3 tool sign` falls back to the key + # configured globally via `wails3 setup`. + - wails3 tool sign --input "{{.BIN_DIR}}/{{.APP_NAME}}*.deb" {{if .PGP_KEY}}--pgp-key "{{.PGP_KEY}}"{{end}} {{if .SIGN_ROLE}}--role "{{.SIGN_ROLE}}"{{end}} + + sign:rpm: + summary: Signs the RPM package + desc: | + Signs the .rpm package with a PGP key. + Set PGP_KEY in the vars section to override the key configured globally via + `wails3 setup` (~/.config/wails/defaults.yaml). + Password is retrieved from system keychain (run: wails3 setup signing) + deps: + - task: create:rpm + cmds: + - wails3 tool sign --input "{{.BIN_DIR}}/{{.APP_NAME}}*.rpm" {{if .PGP_KEY}}--pgp-key "{{.PGP_KEY}}"{{end}} + + sign:packages: + summary: Signs all Linux packages (DEB and RPM) + desc: | + Signs both .deb and .rpm packages with a PGP key. + Set PGP_KEY in the vars section to override the key configured globally via + `wails3 setup` (~/.config/wails/defaults.yaml). + Password is retrieved from system keychain (run: wails3 setup signing) + cmds: + - task: sign:deb + - task: sign:rpm diff --git a/sundynix-desktop/build/linux/appimage/build.sh b/sundynix-desktop/build/linux/appimage/build.sh new file mode 100644 index 0000000..85901c3 --- /dev/null +++ b/sundynix-desktop/build/linux/appimage/build.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Copyright (c) 2018-Present Lea Anthony +# SPDX-License-Identifier: MIT + +# Fail script on any error +set -euxo pipefail + +# Define variables +APP_DIR="${APP_NAME}.AppDir" + +# Create AppDir structure +mkdir -p "${APP_DIR}/usr/bin" +cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/" +cp "${ICON_PATH}" "${APP_DIR}/" +cp "${DESKTOP_FILE}" "${APP_DIR}/" + +if [[ $(uname -m) == *x86_64* ]]; then + # Download linuxdeploy and make it executable + wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + chmod +x linuxdeploy-x86_64.AppImage + + # Run linuxdeploy to bundle the application + ./linuxdeploy-x86_64.AppImage --appdir "${APP_DIR}" --output appimage +else + # Download linuxdeploy and make it executable (arm64) + wget -q -4 -N https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-aarch64.AppImage + chmod +x linuxdeploy-aarch64.AppImage + + # Run linuxdeploy to bundle the application (arm64) + ./linuxdeploy-aarch64.AppImage --appdir "${APP_DIR}" --output appimage +fi + +# Rename the generated AppImage +mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage" + diff --git a/sundynix-desktop/build/linux/desktop b/sundynix-desktop/build/linux/desktop new file mode 100644 index 0000000..36578c2 --- /dev/null +++ b/sundynix-desktop/build/linux/desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Version=1.0 +Name=My Product +Comment=A wails3ref application +# The Exec line includes %u to pass the URL to the application +Exec=/usr/local/bin/wails3ref %u +Terminal=false +Type=Application +Icon=wails3ref +Categories=Utility; +StartupWMClass=wails3ref + + diff --git a/sundynix-desktop/build/linux/nfpm/nfpm.yaml b/sundynix-desktop/build/linux/nfpm/nfpm.yaml new file mode 100644 index 0000000..a9ce634 --- /dev/null +++ b/sundynix-desktop/build/linux/nfpm/nfpm.yaml @@ -0,0 +1,80 @@ +# Feel free to remove those if you don't want/need to use them. +# Make sure to check the documentation at https://nfpm.goreleaser.com +# +# The lines below are called `modelines`. See `:help modeline` + +name: "wails3ref" +arch: ${GOARCH} +platform: "linux" +version: "0.1.0" +section: "default" +priority: "extra" +maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}> +description: "A wails3ref application" +vendor: "My Company" +homepage: "https://wails.io" +license: "MIT" +release: "1" + +contents: + - src: "./bin/wails3ref" + dst: "/usr/local/bin/wails3ref" + - src: "./build/appicon.png" + dst: "/usr/share/icons/hicolor/128x128/apps/wails3ref.png" + - src: "./build/linux/wails3ref.desktop" + dst: "/usr/share/applications/wails3ref.desktop" + +# Default dependencies for the GTK4 + WebKitGTK 6.0 stack (Ubuntu 24.04+ / Debian 13+) +depends: + - libgtk-4-1 + - libwebkitgtk-6.0-4 + +# Distribution-specific overrides for different package formats +overrides: + # RPM packages for Fedora / RHEL / AlmaLinux / Rocky Linux + rpm: + depends: + - gtk4 + - webkitgtk6.0 + + # Arch Linux packages + archlinux: + depends: + - gtk4 + - webkitgtk-6.0 + +# scripts section to ensure desktop database is updated after install +scripts: + postinstall: "./build/linux/nfpm/scripts/postinstall.sh" + # You can also add preremove, postremove if needed + # preremove: "./build/linux/nfpm/scripts/preremove.sh" + # postremove: "./build/linux/nfpm/scripts/postremove.sh" + +# If you build your app with -tags gtk3 (legacy WebKit2GTK 4.1 stack — supported through v3.0.x, removed in v3.1), +# replace the depends/overrides above with these: +# +# depends: +# - libgtk-3-0 +# - libwebkit2gtk-4.1-0 +# overrides: +# rpm: +# depends: +# - gtk3 +# - webkit2gtk4.1 +# archlinux: +# depends: +# - gtk3 +# - webkit2gtk-4.1 +# +# replaces: +# - foobar +# provides: +# - bar +# recommends: +# - whatever +# suggests: +# - something-else +# conflicts: +# - not-foo +# - not-bar +# changelog: "changelog.yaml" diff --git a/sundynix-desktop/build/linux/nfpm/scripts/postinstall.sh b/sundynix-desktop/build/linux/nfpm/scripts/postinstall.sh new file mode 100644 index 0000000..4bbb815 --- /dev/null +++ b/sundynix-desktop/build/linux/nfpm/scripts/postinstall.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# Update desktop database for .desktop file changes +# This makes the application appear in application menus and registers its capabilities. +if command -v update-desktop-database >/dev/null 2>&1; then + echo "Updating desktop database..." + update-desktop-database -q /usr/share/applications +else + echo "Warning: update-desktop-database command not found. Desktop file may not be immediately recognized." >&2 +fi + +# Update MIME database for custom URL schemes (x-scheme-handler) +# This ensures the system knows how to handle your custom protocols. +if command -v update-mime-database >/dev/null 2>&1; then + echo "Updating MIME database..." + update-mime-database -n /usr/share/mime +else + echo "Warning: update-mime-database command not found. Custom URL schemes may not be immediately recognized." >&2 +fi + +exit 0 diff --git a/sundynix-desktop/build/linux/nfpm/scripts/postremove.sh b/sundynix-desktop/build/linux/nfpm/scripts/postremove.sh new file mode 100644 index 0000000..a9bf588 --- /dev/null +++ b/sundynix-desktop/build/linux/nfpm/scripts/postremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/sundynix-desktop/build/linux/nfpm/scripts/preinstall.sh b/sundynix-desktop/build/linux/nfpm/scripts/preinstall.sh new file mode 100644 index 0000000..a9bf588 --- /dev/null +++ b/sundynix-desktop/build/linux/nfpm/scripts/preinstall.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/sundynix-desktop/build/linux/nfpm/scripts/preremove.sh b/sundynix-desktop/build/linux/nfpm/scripts/preremove.sh new file mode 100644 index 0000000..a9bf588 --- /dev/null +++ b/sundynix-desktop/build/linux/nfpm/scripts/preremove.sh @@ -0,0 +1 @@ +#!/bin/bash diff --git a/sundynix-desktop/build/windows/Taskfile.yml b/sundynix-desktop/build/windows/Taskfile.yml new file mode 100644 index 0000000..9cb4e35 --- /dev/null +++ b/sundynix-desktop/build/windows/Taskfile.yml @@ -0,0 +1,195 @@ +version: '3' + +includes: + common: ../Taskfile.yml + +vars: + # Signing configuration - edit these values for your project + # SIGN_CERTIFICATE: "path/to/certificate.pfx" + # SIGN_THUMBPRINT: "certificate-thumbprint" # Alternative to SIGN_CERTIFICATE + # TIMESTAMP_SERVER: "http://timestamp.digicert.com" + # + # Password is stored securely in system keychain. Run: wails3 setup signing + + # Docker image for cross-compilation with CGO (used when CGO_ENABLED=1 on non-Windows) + CROSS_IMAGE: wails-cross + +tasks: + build: + summary: Builds the application for Windows + cmds: + # Auto-detect CGO: if CGO_ENABLED=1, use Docker; otherwise use native Go cross-compile + - task: '{{if and (ne OS "windows") (eq .CGO_ENABLED "1")}}build:docker{{else}}build:native{{end}}' + vars: + ARCH: '{{.ARCH}}' + DEV: '{{.DEV}}' + EXTRA_TAGS: '{{.EXTRA_TAGS}}' + OBFUSCATED: '{{.OBFUSCATED}}' + GARBLE_ARGS: '{{.GARBLE_ARGS}}' + vars: + # Default to CGO_ENABLED=0 if not explicitly set + CGO_ENABLED: '{{.CGO_ENABLED | default "0"}}' + + build:native: + summary: Builds the application using native Go cross-compilation + internal: true + deps: + - task: common:go:mod:tidy + - task: common:build:frontend + vars: + BUILD_FLAGS: + ref: .BUILD_FLAGS + OBFUSCATED: + ref: .OBFUSCATED + DEV: + ref: .DEV + - task: common:generate:icons + preconditions: + - sh: '{{if eq .OBFUSCATED "true"}}command -v garble >/dev/null 2>&1{{else}}true{{end}}' + msg: "garble is required for obfuscated builds. Install it with: go install mvdan.cc/garble@v0.16.0 (requires Go 1.24+). See https://github.com/burrowers/garble/releases for version/toolchain compatibility." + cmds: + - task: generate:syso + vars: + ARCH: '{{.ARCH}}' + - '{{if eq .OBFUSCATED "true"}}garble {{.GARBLE_ARGS}} build{{else}}go build{{end}} {{.BUILD_FLAGS}} -o "{{.BIN_DIR}}/{{.APP_NAME}}.exe"' + - cmd: powershell Remove-item *.syso + platforms: [windows] + - cmd: rm -f *.syso + platforms: [linux, darwin] + vars: + BUILD_FLAGS: '{{if eq .DEV "true"}}{{if or .EXTRA_TAGS (eq .OBFUSCATED "true")}}-tags {{if eq .OBFUSCATED "true"}}wails_obfuscated{{if .EXTRA_TAGS}},{{end}}{{end}}{{.EXTRA_TAGS}} {{end}}-buildvcs=false -gcflags=all="-l"{{else}}-tags production{{if eq .OBFUSCATED "true"}},wails_obfuscated{{end}}{{if .EXTRA_TAGS}},{{.EXTRA_TAGS}}{{end}} -trimpath -buildvcs=false -ldflags="-w -s -H windowsgui"{{end}}' + env: + GOOS: windows + CGO_ENABLED: '{{.CGO_ENABLED | default "0"}}' + GOARCH: '{{.ARCH | default ARCH}}' + + build:docker: + summary: Cross-compiles for Windows using Docker with Zig (for CGO builds on non-Windows) + internal: true + deps: + - task: common:build:frontend + vars: + OBFUSCATED: + ref: .OBFUSCATED + - task: common:generate:icons + preconditions: + - sh: docker info > /dev/null 2>&1 + msg: "Docker is required for CGO cross-compilation. Please install Docker." + - sh: docker image inspect {{.CROSS_IMAGE}} > /dev/null 2>&1 + msg: | + Docker image '{{.CROSS_IMAGE}}' not found. + Build it first: wails3 task setup:docker + cmds: + - task: generate:syso + vars: + ARCH: '{{.ARCH}}' + - docker run --rm -v "{{.ROOT_DIR}}:/app" {{.DOCKER_MOUNTS}} -e APP_NAME="{{.APP_NAME}}" {{if .EXTRA_TAGS}}-e EXTRA_TAGS="{{.EXTRA_TAGS}}"{{end}} {{if eq .OBFUSCATED "true"}}-e OBFUSCATED=true{{end}} {{if .GARBLE_ARGS}}-e GARBLE_ARGS="{{.GARBLE_ARGS}}"{{end}} {{.CROSS_IMAGE}} windows {{.DOCKER_ARCH}} + - cmd: docker run --rm -v "{{.ROOT_DIR}}:/app" alpine chown -R $(id -u):$(id -g) /app/bin + platforms: [linux, darwin] + - rm -f *.syso + vars: + DOCKER_ARCH: '{{.ARCH | default "amd64"}}' + # Generate Docker volume mounts: Go module cache + go.mod replace directives + # Uses wails3 tool docker-mounts for cross-platform compatibility (Windows/Linux/macOS) + DOCKER_MOUNTS: + sh: 'wails3 tool docker-mounts' + + package: + summary: Packages the application + cmds: + - task: '{{if eq (.FORMAT | default "nsis") "msix"}}create:msix:package{{else}}create:nsis:installer{{end}}' + vars: + INSTALL_SCOPE: '{{.INSTALL_SCOPE | default "machine"}}' + vars: + FORMAT: '{{.FORMAT | default "nsis"}}' + INSTALL_SCOPE: '{{.INSTALL_SCOPE | default "machine"}}' + + generate:syso: + summary: Generates Windows `.syso` file + dir: build + cmds: + - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso + vars: + ARCH: '{{.ARCH | default ARCH}}' + + create:nsis:installer: + summary: Creates an NSIS installer + dir: build/windows/nsis + deps: + - task: build + preconditions: + - sh: '[ "{{.INSTALL_SCOPE}}" = "user" ] || [ "{{.INSTALL_SCOPE}}" = "machine" ]' + msg: "INSTALL_SCOPE must be 'user' or 'machine', got '{{.INSTALL_SCOPE}}'" + cmds: + # Create the Microsoft WebView2 bootstrapper if it doesn't exist + - wails3 generate webview2bootstrapper -dir "{{.ROOT_DIR}}/build/windows/nsis" + - | + {{if eq OS "windows"}} + makensis {{if eq .INSTALL_SCOPE "user"}}-DWAILS_INSTALL_SCOPE=user -DREQUEST_EXECUTION_LEVEL=user {{end}}-DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}\{{.BIN_DIR}}\{{.APP_NAME}}.exe" project.nsi + {{else}} + makensis {{if eq .INSTALL_SCOPE "user"}}-DWAILS_INSTALL_SCOPE=user -DREQUEST_EXECUTION_LEVEL=user {{end}}-DARG_WAILS_{{.ARG_FLAG}}_BINARY="{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" project.nsi + {{end}} + vars: + ARCH: '{{.ARCH | default ARCH}}' + ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + INSTALL_SCOPE: '{{.INSTALL_SCOPE | default "machine"}}' + + create:msix:package: + summary: Creates an MSIX package + deps: + - task: build + cmds: + - |- + wails3 tool msix \ + --config "{{.ROOT_DIR}}/wails.json" \ + --name "{{.APP_NAME}}" \ + --executable "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}.exe" \ + --arch "{{.ARCH}}" \ + --out "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}-{{.ARCH}}.msix" \ + {{if .CERT_PATH}}--cert "{{.CERT_PATH}}"{{end}} \ + {{if .PUBLISHER}}--publisher "{{.PUBLISHER}}"{{end}} \ + {{if .USE_MSIX_TOOL}}--use-msix-tool{{else}}--use-makeappx{{end}} + vars: + ARCH: '{{.ARCH | default ARCH}}' + CERT_PATH: '{{.CERT_PATH | default ""}}' + PUBLISHER: '{{.PUBLISHER | default ""}}' + USE_MSIX_TOOL: '{{.USE_MSIX_TOOL | default "false"}}' + + install:msix:tools: + summary: Installs tools required for MSIX packaging + cmds: + - wails3 tool msix-install-tools + + run: + cmds: + - '{{.BIN_DIR}}/{{.APP_NAME}}.exe' + + sign: + summary: Signs the Windows executable + desc: | + Signs the .exe with an Authenticode certificate. + Set SIGN_CERTIFICATE or SIGN_THUMBPRINT to override the certificate + configured globally via `wails3 setup` (~/.config/wails/defaults.yaml). + Password is retrieved from system keychain (run: wails3 setup signing) + deps: + - task: build + cmds: + # Certificate vars are optional: if unset, `wails3 tool sign` falls back to + # the certificate configured globally via `wails3 setup`. + - wails3 tool sign --input "{{.BIN_DIR}}/{{.APP_NAME}}.exe" {{if .SIGN_CERTIFICATE}}--certificate "{{.SIGN_CERTIFICATE}}"{{end}} {{if .SIGN_THUMBPRINT}}--thumbprint "{{.SIGN_THUMBPRINT}}"{{end}} {{if .TIMESTAMP_SERVER}}--timestamp "{{.TIMESTAMP_SERVER}}"{{end}} + + sign:installer: + summary: Signs the NSIS installer + desc: | + Creates and signs the NSIS installer. + Set SIGN_CERTIFICATE or SIGN_THUMBPRINT to override the certificate + configured globally via `wails3 setup` (~/.config/wails/defaults.yaml). + Password is retrieved from system keychain (run: wails3 setup signing) + deps: + - task: create:nsis:installer + vars: + INSTALL_SCOPE: '{{.INSTALL_SCOPE | default "machine"}}' + vars: + INSTALL_SCOPE: '{{.INSTALL_SCOPE | default "machine"}}' + cmds: + - wails3 tool sign --input "build/windows/nsis/{{.APP_NAME}}-installer.exe" {{if .SIGN_CERTIFICATE}}--certificate "{{.SIGN_CERTIFICATE}}"{{end}} {{if .SIGN_THUMBPRINT}}--thumbprint "{{.SIGN_THUMBPRINT}}"{{end}} {{if .TIMESTAMP_SERVER}}--timestamp "{{.TIMESTAMP_SERVER}}"{{end}} diff --git a/sundynix-desktop/build/windows/icon.ico b/sundynix-desktop/build/windows/icon.ico new file mode 100644 index 0000000..bfa0690 Binary files /dev/null and b/sundynix-desktop/build/windows/icon.ico differ diff --git a/sundynix-desktop/build/windows/info.json b/sundynix-desktop/build/windows/info.json new file mode 100644 index 0000000..3402c8e --- /dev/null +++ b/sundynix-desktop/build/windows/info.json @@ -0,0 +1,15 @@ +{ + "fixed": { + "file_version": "0.1.0" + }, + "info": { + "0000": { + "ProductVersion": "0.1.0", + "CompanyName": "My Company", + "FileDescription": "A wails3ref application", + "LegalCopyright": "© 2026, My Company", + "ProductName": "My Product", + "Comments": "This is a comment" + } + } +} \ No newline at end of file diff --git a/sundynix-desktop/build/windows/msix/app_manifest.xml b/sundynix-desktop/build/windows/msix/app_manifest.xml new file mode 100644 index 0000000..891c1df --- /dev/null +++ b/sundynix-desktop/build/windows/msix/app_manifest.xml @@ -0,0 +1,55 @@ + + + + + + + My Product + My Company + A wails3ref application + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sundynix-desktop/build/windows/msix/template.xml b/sundynix-desktop/build/windows/msix/template.xml new file mode 100644 index 0000000..5054ae3 --- /dev/null +++ b/sundynix-desktop/build/windows/msix/template.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + false + My Product + My Company + A wails3ref application + Assets\AppIcon.png + + + + + + + diff --git a/sundynix-desktop/build/windows/nsis/project.nsi b/sundynix-desktop/build/windows/nsis/project.nsi new file mode 100644 index 0000000..754e89a --- /dev/null +++ b/sundynix-desktop/build/windows/nsis/project.nsi @@ -0,0 +1,119 @@ +Unicode true + +#### +## Please note: Template replacements don't work in this file. They are provided with default defines like +## mentioned underneath. +## If the keyword is not defined, "wails_tools.nsh" will populate them. +## If they are defined here, "wails_tools.nsh" will not touch them. This allows you to use this project.nsi manually +## from outside of Wails for debugging and development of the installer. +## +## For development first make a wails nsis build to populate the "wails_tools.nsh": +## > wails build --target windows/amd64 --nsis +## Then you can call makensis on this file with specifying the path to your binary: +## For a AMD64 only installer: +## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app.exe +## For a ARM64 only installer: +## > makensis -DARG_WAILS_ARM64_BINARY=..\..\bin\app.exe +## For a installer with both architectures: +## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app-amd64.exe -DARG_WAILS_ARM64_BINARY=..\..\bin\app-arm64.exe +#### +## The following information is taken from the wails_tools.nsh file, but they can be overwritten here. +#### +## !define INFO_PROJECTNAME "my-project" # Default "wails3ref" +## !define INFO_COMPANYNAME "My Company" # Default "My Company" +## !define INFO_PRODUCTNAME "My Product Name" # Default "My Product" +## !define INFO_PRODUCTVERSION "1.0.0" # Default "0.1.0" +## !define INFO_COPYRIGHT "(c) Now, My Company" # Default "© 2026, My Company" +### +## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe" +## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" +#### +## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html +## !define WAILS_INSTALL_SCOPE "user" # Default "machine" - set to "user" for per-user install ($LOCALAPPDATA) without UAC prompt +#### +## Include the wails tools +#### +!include "wails_tools.nsh" + +# The version information for this two must consist of 4 parts +VIProductVersion "${INFO_PRODUCTVERSION}.0" +VIFileVersion "${INFO_PRODUCTVERSION}.0" + +VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}" +VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer" +VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}" +VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}" +VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}" +VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}" + +# Enable HiDPI support. https://nsis.sourceforge.io/Reference/ManifestDPIAware +ManifestDPIAware true + +!include "MUI.nsh" + +!define MUI_ICON "..\icon.ico" +!define MUI_UNICON "..\icon.ico" +# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314 +!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps +!define MUI_ABORTWARNING # This will warn the user if they exit from the installer. + +!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page. +# !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer +!insertmacro MUI_PAGE_DIRECTORY # In which folder install page. +!insertmacro MUI_PAGE_INSTFILES # Installing page. +!insertmacro MUI_PAGE_FINISH # Finished installation page. + +!insertmacro MUI_UNPAGE_INSTFILES # Uninstalling page + +!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer + +## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1 +#!uninstfinalize 'signtool --file "%1"' +#!finalize 'signtool --file "%1"' + +Name "${INFO_PRODUCTNAME}" +OutFile "..\..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file. +!if "${WAILS_INSTALL_SCOPE}" == "user" + InstallDir "$LOCALAPPDATA\Programs\${INFO_PRODUCTNAME}" +!else + InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" +!endif +ShowInstDetails show # This will always show the installation details. + +Function .onInit + !insertmacro wails.checkArchitecture +FunctionEnd + +Section + !insertmacro wails.setShellContext + + !insertmacro wails.webview2runtime + + SetOutPath $INSTDIR + + !insertmacro wails.files + + CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" + CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" + + !insertmacro wails.associateFiles + !insertmacro wails.associateCustomProtocols + + !insertmacro wails.writeUninstaller +SectionEnd + +Section "uninstall" + !insertmacro wails.setShellContext + + RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath + + RMDir /r $INSTDIR + + Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" + Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk" + + !insertmacro wails.unassociateFiles + !insertmacro wails.unassociateCustomProtocols + + !insertmacro wails.deleteUninstaller +SectionEnd diff --git a/sundynix-desktop/build/windows/nsis/wails_tools.nsh b/sundynix-desktop/build/windows/nsis/wails_tools.nsh new file mode 100644 index 0000000..f583010 --- /dev/null +++ b/sundynix-desktop/build/windows/nsis/wails_tools.nsh @@ -0,0 +1,261 @@ +# DO NOT EDIT - Generated automatically by `wails build` + +!include "x64.nsh" +!include "WinVer.nsh" +!include "FileFunc.nsh" + +!ifndef INFO_PROJECTNAME + !define INFO_PROJECTNAME "wails3ref" +!endif +!ifndef INFO_COMPANYNAME + !define INFO_COMPANYNAME "My Company" +!endif +!ifndef INFO_PRODUCTNAME + !define INFO_PRODUCTNAME "My Product" +!endif +!ifndef INFO_PRODUCTVERSION + !define INFO_PRODUCTVERSION "0.1.0" +!endif +!ifndef INFO_COPYRIGHT + !define INFO_COPYRIGHT "© 2026, My Company" +!endif +!ifndef PRODUCT_EXECUTABLE + !define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe" +!endif +!ifndef UNINST_KEY_NAME + !define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}" +!endif +!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}" + +!ifndef WAILS_INSTALL_SCOPE + !define WAILS_INSTALL_SCOPE "machine" +!endif + +!ifndef REQUEST_EXECUTION_LEVEL + !if "${WAILS_INSTALL_SCOPE}" == "user" + !define REQUEST_EXECUTION_LEVEL "user" + !else + !define REQUEST_EXECUTION_LEVEL "admin" + !endif +!endif + +RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}" + +!ifdef ARG_WAILS_AMD64_BINARY + !define SUPPORTS_AMD64 +!endif + +!ifdef ARG_WAILS_ARM64_BINARY + !define SUPPORTS_ARM64 +!endif + +!ifdef SUPPORTS_AMD64 + !ifdef SUPPORTS_ARM64 + !define ARCH "amd64_arm64" + !else + !define ARCH "amd64" + !endif +!else + !ifdef SUPPORTS_ARM64 + !define ARCH "arm64" + !else + !error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY" + !endif +!endif + +!macro wails.checkArchitecture + !ifndef WAILS_WIN10_REQUIRED + !define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later." + !endif + + !ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED + !define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}" + !endif + + ${If} ${AtLeastWin10} + !ifdef SUPPORTS_AMD64 + ${if} ${IsNativeAMD64} + Goto ok + ${EndIf} + !endif + + !ifdef SUPPORTS_ARM64 + ${if} ${IsNativeARM64} + Goto ok + ${EndIf} + !endif + + IfSilent silentArch notSilentArch + silentArch: + SetErrorLevel 65 + Abort + notSilentArch: + MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}" + Quit + ${else} + IfSilent silentWin notSilentWin + silentWin: + SetErrorLevel 64 + Abort + notSilentWin: + MessageBox MB_OK "${WAILS_WIN10_REQUIRED}" + Quit + ${EndIf} + + ok: +!macroend + +!macro wails.files + !ifdef SUPPORTS_AMD64 + ${if} ${IsNativeAMD64} + File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}" + ${EndIf} + !endif + + !ifdef SUPPORTS_ARM64 + ${if} ${IsNativeARM64} + File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}" + ${EndIf} + !endif +!macroend + +!macro wails.writeUninstaller + WriteUninstaller "$INSTDIR\uninstall.exe" + + SetRegView 64 + !if "${WAILS_INSTALL_SCOPE}" == "user" + WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}" + WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}" + WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" + + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0" + !else + WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}" + WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}" + WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" + + ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 + IntFmt $0 "0x%08X" $0 + WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0" + !endif +!macroend + +!macro wails.deleteUninstaller + Delete "$INSTDIR\uninstall.exe" + + SetRegView 64 + !if "${WAILS_INSTALL_SCOPE}" == "user" + DeleteRegKey HKCU "${UNINST_KEY}" + !else + DeleteRegKey HKLM "${UNINST_KEY}" + !endif +!macroend + +!macro wails.setShellContext + ${If} ${REQUEST_EXECUTION_LEVEL} == "admin" + SetShellVarContext all + ${else} + SetShellVarContext current + ${EndIf} +!macroend + +# Install webview2 by launching the bootstrapper +# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment +!macro wails.webview2runtime + !ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT + !define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime" + !endif + + SetRegView 64 + # If the admin key exists and is not empty then webview2 is already installed + ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ${If} $0 != "" + Goto ok + ${EndIf} + + ${If} ${REQUEST_EXECUTION_LEVEL} == "user" + # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed + ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ${If} $0 != "" + Goto ok + ${EndIf} + ${EndIf} + + SetDetailsPrint both + DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}" + SetDetailsPrint listonly + + InitPluginsDir + CreateDirectory "$pluginsdir\webview2bootstrapper" + SetOutPath "$pluginsdir\webview2bootstrapper" + File "MicrosoftEdgeWebview2Setup.exe" + ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install' + + SetDetailsPrint both + ok: +!macroend + +# Copy of APP_ASSOCIATE and APP_UNASSOCIATE macros from here https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b +!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND + ; Backup the previously associated file class + ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0" + + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}" + + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open" + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}` +!macroend + +!macro APP_UNASSOCIATE EXT FILECLASS + ; Backup the previously associated file class + ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup` + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0" + + DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}` +!macroend + +!macro wails.associateFiles + ; Create file associations + +!macroend + +!macro wails.unassociateFiles + ; Delete app associations + +!macroend + +!macro CUSTOM_PROTOCOL_ASSOCIATE PROTOCOL DESCRIPTION ICON COMMAND + DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "" "${DESCRIPTION}" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "URL Protocol" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\DefaultIcon" "" "${ICON}" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell" "" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open" "" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open\command" "" "${COMMAND}" +!macroend + +!macro CUSTOM_PROTOCOL_UNASSOCIATE PROTOCOL + DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}" +!macroend + +!macro wails.associateCustomProtocols + ; Create custom protocols associations + +!macroend + +!macro wails.unassociateCustomProtocols + ; Delete app custom protocol associations + +!macroend \ No newline at end of file diff --git a/sundynix-desktop/build/windows/wails.exe.manifest b/sundynix-desktop/build/windows/wails.exe.manifest new file mode 100644 index 0000000..2c06828 --- /dev/null +++ b/sundynix-desktop/build/windows/wails.exe.manifest @@ -0,0 +1,22 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + + + + + + + \ No newline at end of file diff --git a/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/app.js b/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/app.js new file mode 100644 index 0000000..080e68b --- /dev/null +++ b/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/app.js @@ -0,0 +1,62 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +/** + * App 经 Wails v3 的 Service 绑定暴露给前端,承载只有桌面端能做的原生能力: + * 文件读写、系统"另存为"框、用系统默认应用打开、原生通知。 + * v3 中不再需要注入 ctx——对话框经 application.Get().Dialog 获取。 + * @module + */ + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Call as $Call, CancellablePromise as $CancellablePromise, Create as $Create } from "@wailsio/runtime"; + +/** + * Notify 弹一条系统通知(best-effort:macOS 用 osascript,其它平台暂静默)。 + * @param {string} title + * @param {string} body + * @returns {$CancellablePromise} + */ +export function Notify(title, body) { + return $Call.ByID(2739682372, title, body); +} + +/** + * OpenReport 把报告下载到临时目录,并用系统默认应用(Word/Pages/WPS)打开。 + * @param {string} url + * @param {string} filename + * @returns {$CancellablePromise} + */ +export function OpenReport(url, filename) { + return $Call.ByID(802670751, url, filename); +} + +/** + * Ping 供前端探活 Go 桥是否就绪。 + * @returns {$CancellablePromise} + */ +export function Ping() { + return $Call.ByID(1003072145); +} + +/** + * ReadLocalFile 读取本地文件内容(本地文件系统 I/O)。 + * @param {string} path + * @returns {$CancellablePromise} + */ +export function ReadLocalFile(path) { + return $Call.ByID(4016121990, path); +} + +/** + * SaveReportAs 弹原生"另存为"对话框,把 url 指向的报告(.docx)下载到用户选定路径。 + * 返回保存路径;用户取消则返回空串。 + * @param {string} url + * @param {string} filename + * @returns {$CancellablePromise} + */ +export function SaveReportAs(url, filename) { + return $Call.ByID(1437856486, url, filename); +} diff --git a/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/index.js b/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/index.js new file mode 100644 index 0000000..9c918c2 --- /dev/null +++ b/sundynix-desktop/frontend/bindings/github.com/sundynix/sundynix-desktop/index.js @@ -0,0 +1,8 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +import * as App from "./app.js"; +export { + App +}; diff --git a/sundynix-desktop/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.js b/sundynix-desktop/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.js new file mode 100644 index 0000000..1ea1058 --- /dev/null +++ b/sundynix-desktop/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.js @@ -0,0 +1,9 @@ +//@ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore: Unused imports +import { Create as $Create } from "@wailsio/runtime"; + +Object.freeze($Create.Events); diff --git a/sundynix-desktop/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts b/sundynix-desktop/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts new file mode 100644 index 0000000..3dd1807 --- /dev/null +++ b/sundynix-desktop/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts @@ -0,0 +1,2 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT diff --git a/sundynix-desktop/frontend/package-lock.json b/sundynix-desktop/frontend/package-lock.json index 964e6e6..8357b52 100644 --- a/sundynix-desktop/frontend/package-lock.json +++ b/sundynix-desktop/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "sundynix-desktop-frontend", "version": "0.1.0", "dependencies": { + "@wailsio/runtime": "^3.0.0-alpha.97", "@xyflow/react": "^12.3.0", "lucide-react": "^1.17.0", "react": "^19.0.0", @@ -2142,6 +2143,12 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@wailsio/runtime": { + "version": "3.0.0-alpha.97", + "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.97.tgz", + "integrity": "sha512-alvEG6B0YqW7AcBcNaUWNMD9q8JWqx5AO6BPOZpijBeq4ZjhQxSom3jvvq8Z1mWSMKPEjSvlFirKIh9NLI3ajA==", + "license": "MIT" + }, "node_modules/@xyflow/react": { "version": "12.11.0", "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.11.0.tgz", diff --git a/sundynix-desktop/frontend/package.json b/sundynix-desktop/frontend/package.json index 7d7f57d..e17911c 100644 --- a/sundynix-desktop/frontend/package.json +++ b/sundynix-desktop/frontend/package.json @@ -11,6 +11,7 @@ "test:watch": "vitest" }, "dependencies": { + "@wailsio/runtime": "^3.0.0-alpha.97", "@xyflow/react": "^12.3.0", "lucide-react": "^1.17.0", "react": "^19.0.0", diff --git a/sundynix-desktop/frontend/src/lib/desktop.ts b/sundynix-desktop/frontend/src/lib/desktop.ts index 9313711..4ca860d 100644 --- a/sundynix-desktop/frontend/src/lib/desktop.ts +++ b/sundynix-desktop/frontend/src/lib/desktop.ts @@ -1,50 +1,44 @@ -// 桌面端原生能力桥 —— 经 Wails 注入的 window.go.main.App 调 Go 方法。 -// 关键:在浏览器预览(make web,无 Wails 运行时)下 window.go 不存在, -// 所有方法自动降级为纯 Web 行为,保证两种模式都能跑。 +// 桌面端原生能力桥 —— Wails v3 Service 绑定(App)调 Go 方法。 +// 关键:在浏览器预览(无 Wails 运行时)下自动降级为纯 Web 行为,保证两种模式都能跑。 +// v3 检测:原生启动时注入 window._wails.environment;浏览器没有,据此判定是否在桌面壳内。 +import { App } from "../../bindings/github.com/sundynix/sundynix-desktop"; +import { System } from "@wailsio/runtime"; -interface WailsApp { - SaveReportAs(url: string, filename: string): Promise; - OpenReport(url: string, filename: string): Promise; - Notify(title: string, body: string): Promise; -} - -function app(): WailsApp | null { - return ((window as unknown as { go?: { main?: { App?: WailsApp } } }).go?.main?.App as WailsApp) ?? null; +function inWails(): boolean { + return !!(window as unknown as { _wails?: { environment?: unknown } })._wails?.environment; } // isDesktop 是否运行在真实 Wails 桌面窗口(而非浏览器预览)。 export function isDesktop(): boolean { - return !!app(); + return inWails(); } // isMacDesktop 用于交通灯让位等 macOS 专属适配。 export function isMacDesktop(): boolean { - return isDesktop() && /Mac/i.test(navigator.userAgent); + return inWails() && System.IsMac(); } // saveReportAs:桌面端弹原生"另存为"框落盘;浏览器降级为触发下载。返回保存路径(浏览器/取消为空)。 export async function saveReportAs(url: string, filename: string): Promise { - const a = app(); - if (!a) { + if (!inWails()) { triggerDownload(url, filename); return ""; } - return a.SaveReportAs(url, filename); + return App.SaveReportAs(url, filename); } // openReport:桌面端下载到临时目录并用系统默认应用打开;浏览器降级为新标签打开。 export async function openReport(url: string, filename: string): Promise { - const a = app(); - if (!a) { + if (!inWails()) { window.open(url, "_blank"); return; } - return a.OpenReport(url, filename); + await App.OpenReport(url, filename); } // notify:桌面端弹系统通知;浏览器为空操作(由应用内 Toast 兜底)。 export function notify(title: string, body: string): void { - app()?.Notify(title, body); + if (inWails()) void App.Notify(title, body); } // printReportHtml:把已渲染的报告 HTML 在打印视图里出 PDF(浏览器/Webview 的"打印→存为 PDF")。 diff --git a/sundynix-desktop/frontend/src/lib/version.test.ts b/sundynix-desktop/frontend/src/lib/version.test.ts index 689a552..bff77ad 100644 --- a/sundynix-desktop/frontend/src/lib/version.test.ts +++ b/sundynix-desktop/frontend/src/lib/version.test.ts @@ -1,6 +1,10 @@ import { describe, it, expect, vi, afterEach } from "vitest"; +import { Browser } from "@wailsio/runtime"; import { isNewer, checkUpdate, openExternal, APP_VERSION } from "./version"; +// mock v3 runtime 的 Browser.OpenURL(openExternal 桌面态走它)。 +vi.mock("@wailsio/runtime", () => ({ Browser: { OpenURL: vi.fn() } })); + describe("isNewer(语义化版本比较)", () => { it.each([ ["1.0.1", "1.0.0", true], @@ -60,20 +64,17 @@ describe("checkUpdate", () => { describe("openExternal", () => { afterEach(() => { - delete (window as unknown as { runtime?: unknown }).runtime; - vi.restoreAllMocks(); + delete (window as unknown as { _wails?: unknown })._wails; + vi.clearAllMocks(); }); - it("有 Wails runtime → 走 BrowserOpenURL", () => { - const spy = vi.fn(); - (window as unknown as { runtime: { BrowserOpenURL: (u: string) => void } }).runtime = { - BrowserOpenURL: spy, - }; + it("桌面态(_wails.environment 存在)→ 走 Browser.OpenURL", () => { + (window as unknown as { _wails: { environment: { OS: string } } })._wails = { environment: { OS: "darwin" } }; openExternal("https://a"); - expect(spy).toHaveBeenCalledWith("https://a"); + expect(Browser.OpenURL).toHaveBeenCalledWith("https://a"); }); - it("无 runtime(浏览器模式)→ 回退 window.open", () => { + it("浏览器模式(无 _wails.environment)→ 回退 window.open", () => { const open = vi.spyOn(window, "open").mockImplementation(() => null); openExternal("https://b"); expect(open).toHaveBeenCalledWith("https://b", "_blank"); diff --git a/sundynix-desktop/frontend/src/lib/version.ts b/sundynix-desktop/frontend/src/lib/version.ts index b367fa0..21adda4 100644 --- a/sundynix-desktop/frontend/src/lib/version.ts +++ b/sundynix-desktop/frontend/src/lib/version.ts @@ -1,5 +1,6 @@ // 桌面端版本与"检查更新"——查 GitHub Releases 最新版,比当前版本新则提示下载。 // 发版流程:bump 此处 APP_VERSION(= package.json version)→ 打 tag vX.Y.Z → release workflow 自动构建+发布。 +import { Browser } from "@wailsio/runtime"; export const APP_VERSION = "0.1.0"; // 当前桌面端版本(与 git tag 去掉 v 前缀对齐) @@ -27,11 +28,10 @@ export async function checkUpdate(): Promise { } } -// openExternal 在系统浏览器打开链接(Wails 用 runtime.BrowserOpenURL,浏览器模式回退 window.open)。 +// openExternal 在系统浏览器打开链接(Wails v3 用 Browser.OpenURL,浏览器模式回退 window.open)。 export function openExternal(url: string): void { - const w = window as unknown as { runtime?: { BrowserOpenURL?: (u: string) => void } }; - if (w.runtime?.BrowserOpenURL) { - w.runtime.BrowserOpenURL(url); + if ((window as unknown as { _wails?: { environment?: unknown } })._wails?.environment) { + void Browser.OpenURL(url); } else { window.open(url, "_blank"); } diff --git a/sundynix-desktop/frontend/tsconfig.json b/sundynix-desktop/frontend/tsconfig.json index 276aaf2..83476fd 100644 --- a/sundynix-desktop/frontend/tsconfig.json +++ b/sundynix-desktop/frontend/tsconfig.json @@ -5,6 +5,8 @@ "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, + "allowJs": true, + "checkJs": false, "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, diff --git a/sundynix-desktop/frontend/vite.config.ts b/sundynix-desktop/frontend/vite.config.ts index a62d0bc..b1a3d3d 100644 --- a/sundynix-desktop/frontend/vite.config.ts +++ b/sundynix-desktop/frontend/vite.config.ts @@ -1,10 +1,16 @@ /// import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import wails from "@wailsio/runtime/plugins/vite"; import path from "node:path"; export default defineConfig({ - plugins: [react()], + // wails3 dev 传 WAILS_VITE_PORT(默认 9245);浏览器预览(纯 vite)回退 5173。 + server: { + port: Number(process.env.WAILS_VITE_PORT) || 5173, + strictPort: !!process.env.WAILS_VITE_PORT, + }, + plugins: [react(), wails("./bindings")], resolve: { alias: { "@": path.resolve(__dirname, "./src") }, }, diff --git a/sundynix-desktop/go.mod b/sundynix-desktop/go.mod index 4e2ca41..5d9b495 100644 --- a/sundynix-desktop/go.mod +++ b/sundynix-desktop/go.mod @@ -1,36 +1,16 @@ module github.com/sundynix/sundynix-desktop -go 1.23 +go 1.25.0 -require github.com/wailsapp/wails/v2 v2.12.0 +require github.com/wailsapp/wails/v3 v3.0.0-alpha2.117 require ( - git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect - github.com/bep/debounce v1.2.1 // indirect + github.com/adrg/xdg v0.5.3 // indirect + github.com/coder/websocket v1.8.14 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.3 // indirect - github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect - github.com/labstack/echo/v4 v4.13.3 // indirect - github.com/labstack/gommon v0.4.2 // indirect - github.com/leaanthony/go-ansi-parser v1.6.1 // indirect - github.com/leaanthony/gosod v1.0.4 // indirect - github.com/leaanthony/slicer v1.6.0 // indirect - github.com/leaanthony/u v1.1.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/godbus/dbus/v5 v5.2.2 // indirect + github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/samber/lo v1.49.1 // indirect - github.com/tkrajina/go-reflector v0.5.8 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.2 // indirect - github.com/wailsapp/go-webview2 v1.0.22 // indirect - github.com/wailsapp/mimetype v1.4.1 // indirect - golang.org/x/crypto v0.33.0 // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/sys v0.44.0 // indirect ) diff --git a/sundynix-desktop/go.sum b/sundynix-desktop/go.sum index b5aa9ee..708c15e 100644 --- a/sundynix-desktop/go.sum +++ b/sundynix-desktop/go.sum @@ -1,83 +1,35 @@ -git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA= -git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc= -github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= -github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= +github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e h1:Lf/gRkoycfOBPa42vU2bbgPurFong6zXeFtPoxholzU= +github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e/go.mod h1:uNVvRXArCGbZ508SxYYTC5v1JWoz2voff5pm25jU1Ok= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= -github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= -github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= -github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= -github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= -github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A= -github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= -github.com/leaanthony/gosod v1.0.4 h1:YLAbVyd591MRffDgxUOU1NwLhT9T1/YiwjKZpkNFeaI= -github.com/leaanthony/gosod v1.0.4/go.mod h1:GKuIL0zzPj3O1SdWQOdgURSuhkF+Urizzxh26t9f1cw= -github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= -github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= -github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= -github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= +github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ= +github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= -github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ= -github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= -github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58= -github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= -github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= -github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= -github.com/wailsapp/wails/v2 v2.12.0 h1:BHO/kLNWFHYjCzucxbzAYZWUjub1Tvb4cSguQozHn5c= -github.com/wailsapp/wails/v2 v2.12.0/go.mod h1:mo1bzK1DEJrobt7YrBjgxvb5Sihb1mhAY09hppbibQg= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/wailsapp/wails/v3 v3.0.0-alpha2.117 h1:udyjqPG3AIgkod5QDR/WblCkpV8R86BFPSrsWxSyt5Y= +github.com/wailsapp/wails/v3 v3.0.0-alpha2.117/go.mod h1:74WH2FScMsgucZvHHvv7eOefDXCm/CjuIxqhhZgPhKg= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sundynix-desktop/main.go b/sundynix-desktop/main.go index 3847276..79d70cc 100644 --- a/sundynix-desktop/main.go +++ b/sundynix-desktop/main.go @@ -1,34 +1,47 @@ -// Command sundynix-desktop —— 第 1 层客户端,Wails 原生桌面应用入口。 +// Command sundynix-desktop —— 第 1 层客户端,Wails v3 原生桌面应用入口。 package main import ( "embed" + "log" - "github.com/wailsapp/wails/v2" - "github.com/wailsapp/wails/v2/pkg/options" - "github.com/wailsapp/wails/v2/pkg/options/assetserver" - "github.com/wailsapp/wails/v2/pkg/options/mac" + "github.com/wailsapp/wails/v3/pkg/application" ) //go:embed all:frontend/dist var assets embed.FS func main() { - app := NewApp() - _ = wails.Run(&options.App{ + // v3:application.New + Service 绑定 + 显式 Window。 + app := application.New(application.Options{ + Name: "sundynix-agentix", + Description: "事件驱动的 AI Agent 工作台", + Services: []application.Service{ + application.NewService(&App{}), // 暴露原生能力(文件/另存为/打开/通知)给前端 + }, + Assets: application.AssetOptions{ + Handler: application.AssetFileServerFS(assets), + }, + Mac: application.MacOptions{ + ApplicationShouldTerminateAfterLastWindowClosed: true, + }, + }) + + app.Window.NewWithOptions(application.WebviewWindowOptions{ Title: "sundynix-agentix", Width: 1440, Height: 900, MinWidth: 1100, MinHeight: 700, - BackgroundColour: &options.RGBA{R: 11, G: 13, B: 18, A: 1}, // 与深色主题一致 #0b0d12 - AssetServer: &assetserver.Options{Assets: assets}, - // macOS:隐藏标题栏、内容铺满到顶(保留红绿灯交通灯),顶栏自定义为可拖拽区。 - Mac: &mac.Options{ - TitleBar: mac.TitleBarHiddenInset(), + BackgroundColour: application.NewRGB(11, 13, 18), // 与深色主题一致 #0b0d12 + // macOS:隐藏标题栏、内容铺满到顶(保留交通灯),顶栏自定义为可拖拽区。 + Mac: application.MacWindow{ + TitleBar: application.MacTitleBarHiddenInset, }, - OnStartup: app.startup, - // Bind: TS/Go 强绑定 —— 把 App 的方法暴露给前端 - Bind: []any{app}, + URL: "/", }) + + if err := app.Run(); err != nil { + log.Fatal(err) + } } diff --git a/sundynix-desktop/wails.json b/sundynix-desktop/wails.json deleted file mode 100644 index 815426f..0000000 --- a/sundynix-desktop/wails.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://wails.io/schemas/config.v2.json", - "name": "sundynix_desktop", - "outputfilename": "sundynix_desktop", - "frontend:install": "npm install", - "frontend:build": "npm run build", - "frontend:dev:watcher": "npm run dev", - "frontend:dev:serverUrl": "auto", - "author": { - "name": "sundynix" - } -}